95 Commits
Author SHA1 Message Date
Nick Vigilante c58ac71ca3 docs(docs/tutorials): reformat to one sentence per line (#30703)
Reformats the tutorials under `docs/tutorials` to one sentence per line,
as the [docs style
guide](https://github.com/coder/coder/blob/main/docs/.style/style-guide/formatting.md#one-sentence-per-line)
requires. Hard-wrapped paragraphs are unwrapped and lines holding
several sentences are split, so a later edit to one sentence shows up as
a one-line diff.

Apart from the exception below, only whitespace inside paragraphs and
list items changes, and the rendered pages are the same. Comparing the
changed files against `main` word for word, ignoring whitespace and
blockquote `>` markers, finds differences only on the reworded lines.
`git diff --word-diff` is easier to read than the line diff.

9 lines that already contained an em or en dash were reworded by hand,
because `lint/emdash` treats every reflowed line as newly added. Each
uses a parenthetical, a separate sentence, or a colon after a lead-in.
Dashes on lines the reflow did not touch are left alone. The two reverse
proxy tutorials also write `*.coder.example.com` in backticks instead of
escaping the asterisk, which the formatter would otherwise drop.

Applied with an in-progress formatter, limited to pages listed in
`docs/manifest.json` and excluding generated reference docs. This is one
of a batch of PRs that split the docs by area (19 files changed, 656
insertions(+), 1265 deletions(-)).

---
🤖 Built with AI assistance.
2026-10-15 11:24:40 -04:00
Nick Vigilante 935e076210 docs: delete unlisted pages and their screenshots (#30758)
Deletes seven docs files and 10 screenshots that are not in the docs
manifest and are superseded or empty:

- `docs/about/why-coder.md`: a TODO stub. The one link to it, a "Do"
example in the style guide, now names its source in plain text.
- `docs/tutorials/example-guide.md`: a contributor template with no
inbound links. Its guidance is recorded in DOCS-1207 for a tutorial
section in `contributing/documentation.md`, which is still to write.
- `docs/start/first-template.md` and six step 6 screenshots in
`docs/images/templates/`. Content is cataloged in DOCS-1209 for the
Templates landing page.
- `docs/start/first-workspace.md` and three screenshots in
`docs/images/start/`. Content is cataloged in DOCS-1208 for the
Workspaces landing page.
- `docs/start/local-deploy.md` and `docs/images/start/setup-page.png`.
Redundant with `get-started/` and `install/cli.md`.

The deleted files stay recoverable at `adcce66826`.

This replaces #30710, #30711, #30712, #30713 and #30714, which are
closed in favor of this PR.

Linear: DOCS-1205, DOCS-1206, DOCS-1207, DOCS-1211, DOCS-1212. Related:
DOCS-1208, DOCS-1209.

I ran `lint/docs-html`, `lint/docs-manifest`, `lint/docs-redirects` and
`lint/emdash` locally.

> Generated by Coder Agents on behalf of @nickvigilante.
2026-10-14 14:03:58 -04:00
blinkagent[bot]andblink-so[bot] c9680e4f36 docs: correct the CODER_AGENT_BLOCK_FILE_TRANSFER block list (#29705)
## Problem

The FAQ entry _"How can I restrict inbound/outbound file transfers from
Coder workspaces?"_ documents the wrong block list for
`CODER_AGENT_BLOCK_FILE_TRANSFER`.

It currently says the list includes `scp`, `rsync`, `ftp`, and `nc`.

The actual list in
[`agent/agentssh/agentssh.go`](https://github.com/coder/coder/blob/main/agent/agentssh/agentssh.go)
is:

```go
// BlockedFileTransferCommands contains a list of restricted file transfer commands.
var BlockedFileTransferCommands = []string{"nc", "rsync", "scp", "sftp"}
```

So the docs have two errors:

- **`ftp` is documented but is not blocked.** An admin reading this page
could reasonably believe `ftp` is covered when it is not.
- **`sftp` is blocked but is not documented.** This matters
operationally, because SFTP is blanket-blocked at the subsystem level
(`if session.Subsystem() == "sftp"`), which disables anything built on
it.

## Changes

1. Correct the block list to match the source, in the same order as the
slice so the two are easy to diff by eye in future:

```diff
-block list, which includes `scp`, `rsync`, `ftp`, and `nc`.
+block list, which includes `nc`, `rsync`, `scp`, and `sftp`.
```

2. Per review feedback, add a note to **Important Notice** covering what
enabling this actually breaks: SFTP-based tooling, and [Coder Desktop
file
sync](https://coder.com/docs/user-guides/desktop/desktop-connect-sync).

Desktop file sync breaks via `scp` rather than `sftp`. It runs Mutagen
over SSH, and Mutagen's SSH transport installs its remote agent binary
by shelling out to `scp`
([`pkg/agent/transport/ssh/transport.go`](https://github.com/coder/mutagen/blob/v0.18.3/pkg/agent/transport/ssh/transport.go)),
which is on the block list. The note keeps that distinction because an
admin debugging it will see an `scp` failure, not an SFTP one.

## Correction

An earlier revision of this description claimed `coder cp` is also
blocked. **That was wrong — there is no `coder cp` command.** No
`cli/cp.go`, no `docs/reference/cli/cp.md`, no `cp` entry in the CLI
index. I've removed the claim rather than document a command that
doesn't exist. Discussed in the thread below.

## Not included

Linking the FAQ to [Agent
Firewall](https://coder.com/docs/ai-coder/agent-firewall) from the "For
more advanced security needs" line, which currently only says "consider
adopting an endpoint security solution." Happy to add if wanted, but it
felt out of scope for a correctness fix.

Also note I only verified the macOS Desktop client.
`coder-desktop-windows` and `coder-desktop-linux` are very likely the
same Mutagen setup, but I didn't read them, so the note makes no
per-platform claim.

Created on behalf of @bartekgatzcoder.

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
2026-10-07 15:02:55 +00:00
Nick Vigilante 28cf704f27 chore: update references to the relocated install docs paths (#30380)
The Install docs moved into the five-phase spine in #29304, but several
references outside `docs/` still point at the pre-move URLs.
`docs/redirects.json` is empty, so those URLs 404 rather than
forwarding.
The release note generators are the user-visible ones: every generated
release note links `coder.com/docs/install/releases` and
`coder.com/docs/install/upgrade`.

| Reference | Was | Now |
|---|---|---|
| Release schedule links in release notes | `/docs/install/releases` |
`/docs/reference/releases` |
| Upgrade link in release notes | `/docs/install/upgrade` |
`/docs/install/operate/upgrade` |
| README sizing link |
`/docs/admin/infrastructure/validated-architectures` |
`/docs/install/plan/sizing` |
| Provisioner tag table comments | `docs/admin/provisioners/index.md` |
`docs/install/operate/provisioners/index.md` |
| FAQ provenance comment | `docs/admin/licensing/index.md` |
`docs/install/prepare/licensing.md` |
| ESR 2.24-2.29 upgrade table | `/docs/admin/provisioners` |
`/docs/install/operate/provisioners` |
| Helm provisioner README, `NOTES.txt`, and `values.yaml` |
`/docs/admin/provisioners` | `/docs/install/operate/provisioners` |
| Helm Coder chart README | `/docs/install/kubernetes` |
`/docs/install/server/kubernetes` |
| `workspace-tags` example README |
`/docs/admin/provisioners#external-provisioners` |
`/docs/install/operate/provisioners#types-of-provisioners` |
| `kubernetes-envbox` example README |
`/docs/admin/infrastructure/architecture#agents` |
`/docs/install/plan/architecture#agents` |

`scripts/releaser/docs.go` was already updated in #29304; this covers
what that pass missed.

Two notes on anchors:

- The `#external-provisioners` anchor was already dead before #29304, so
it now points at `#types-of-provisioners`, the section covering
external, organization-scoped, and user-scoped provisioners.
- Every other anchor was verified to still exist on its new page.

One link left alone: `provisionerd/provisionerd.go` references
`docs/admin/provisioners.md` through a pinned-commit GitHub permalink,
which still resolves and shouldn't be rewritten.

## Verification

- `go test ./scripts/release/...` passes, including the release note
golden strings.
- `go build ./scripts/releaser/` succeeds.
- `make pre-commit` passes.
- Markdown table padding reflowed with `markdown-table-formatter`;
`markdownlint` reports no new findings.

<sub>Opened by Coder Agents on behalf of @nickvigilante.</sub>
2026-10-05 22:28:54 +00:00
Nick Vigilante 0d1225694e feat: show coder sharing in CLI help and reference docs (#30141)
`coder sharing` has been hidden since it was added behind an experiment,
although Shared Workspaces is GA and the prose docs already use it.

This removes `Hidden: true`, regenerates `docs/reference/cli/sharing/*`,
and adds the pages to the docs manifest.
The shared workspaces guide and the persistent shared workspaces
tutorial now link to the new reference.

**Behavior change:** `coder sharing` and its subcommands now appear in
`coder --help`.
The commands themselves are unchanged.
The `sharing remove --user/--group` help text previously said "to share
the workspace with"; it now says "to remove from the workspace".

<details>
<summary>Decisions</summary>

- Follow-up not in this PR: `DELETE /workspaces/{workspace}/acl` has no
CLI caller.

</details>

Closes [CLI-22](https://linear.app/codercom/issue/CLI-22)

_This PR was generated by Coder Agents on behalf of @nickvigilante._
2026-09-30 21:29:51 +00:00
Nick Vigilante f1ed84ffab docs: author the sidebar in per-section YAML compiled to manifest.json (#30165)
## Summary

The docs sidebar is now written as YAML files under `docs/manifest/`,
one route and its direct children per file, and a new tool,
`scripts/docsmanifestgen`, compiles them into `docs/manifest.json`.
Moving two top-level sections is now a 2-line change in
`docs/manifest/index.yml` instead of a diff of hundreds of lines in a
2,600-line JSON file.
The compiled `docs/manifest.json` is byte-identical to `main`, so
coder.com, docs-preview, deploy-docs, and offlinedocs need no changes.

Refs
[DOCS-1131](https://linear.app/codercom/issue/DOCS-1131/author-the-docs-sidebar-in-per-section-yaml-compiled-to)

## Changes

- **Sources:** `docs/manifest/index.yml` holds `versions` and the
section files in sidebar order.
Each section file (`about.yml`, `admin.yml`, and so on) holds one
top-level route.
- **One level per file:** a file holds one route and its direct
children.
A child with children of its own, or with `children_from`, lives in its
own file in a directory named after the parent file, and the parent
lists it as `- include: admin/users.yml`.
For example, `get-started.yml` includes
`get-started/customize-your-template.yml`, which lists that page's
children.
  The 8 section files split into 53 source files.
- **`scripts/docsmanifestgen`:** `build` compiles the sources into
`docs/manifest.json`, `fmt` formats them, and `check` validates them.
- Keys must be known (a typo like `titel:` fails with a line number) and
appear in a fixed order in both the YAML and the JSON.
- Every `path` must exist, and every file under `docs/manifest/` must be
referenced from `index.yml`, an `include`, or a `children_from`.
- A grandchild in the same file fails with a message to move the child
into its own file, and an `include` entry can't set other keys or name a
file that is already used.
- A comment on the same line as a `title` or `description` is rejected,
because an unquoted ` #` would silently cut off the value.
- **Generated fragments:** `clidocgen` and `apidocgen` no longer rewrite
the manifest.
They write the Command Line and REST API children to
`docs/manifest/generated/*.json`, which the sources pull in with
`children_from`.
  The tool accepts fragments as YAML or JSON.
- The hand-written REST API descriptions and states moved into the YAML,
attached to generated routes by title.
A YAML entry that matches no generated route, or disagrees with one,
fails the build instead of being dropped silently.
- **Shared code:** loading, validation, and fragment merging live in
`scripts/docgenenv/sources.go`, so the generators and the tool share one
loader.
`docgenenv.Route` gains YAML tags and the source-only `ChildrenFrom` and
`Include` fields (`json:"-"`).
`LoadManifest` is removed because nothing reads the compiled JSON
anymore.
- **Makefile:**
- `docs/manifest.json` is built from the sources and fragments, which
replaces the `_gen/manifest-staging.json` handoff.
- Each fragment has its own rule, and the fragments are added to the
`.PRECIOUS`, `GEN_FILES`, and `gen/mark-fresh` lists.
  - `DOCS_MANIFEST_SOURCES` finds the source files recursively.
- New targets: `fmt/docs-manifest` (part of `fmt`), `lint/docs-manifest`
(part of `lint`), and `gen/docs-manifest`, the quick path after a
sidebar-only edit that skips regenerating the CLI and API fragments.
- **`.gitattributes`:** marks `docs/manifest.json` and
`docs/manifest/generated/*` as generated so GitHub collapses them in
diffs.
- **Guidance:** the content guidelines, the style guide,
`DOCS_STYLE_GUIDE.md`, the `write-docs` and `doc-check` skills,
`docs/README.md`, and `docs/tutorials/example-guide.md` now point to the
YAML sources instead of `docs/manifest.json`.

## Testing

- Split today's `docs/manifest.json` into the sources, rebuilt it, and
confirmed the output is byte-identical to `main`.
After splitting the sources to one level per file, the rebuilt
`docs/manifest.json` and a fresh `clidocgen` CLI fragment were still
byte-identical, and `LoadManifestSources` still finds the REST API and
Command Line routes through their includes.
After rebasing onto `main`, both commits rebuild `main`'s
`docs/manifest.json` byte for byte.
- Hand-edited `docs/manifest.json` and a YAML source, then rebuilt: the
YAML change persisted and the JSON edit was overwritten.
CI's `make -B gen` plus `check_unstaged.sh` fails on a committed hand
edit to the JSON.
- Deleted both fragments and ran `make docs/manifest.json` in a fresh
worktree, which regenerated them through swag and `clidocgen`.
`docs/manifest.json`, the fragments, and every generated CLI and API
page had no diff.
- Swapped User Guides and Administration in `index.yml`: 2 changed lines
in the source, 166 generated lines in `docs/manifest.json`.
  Swapping back restored the file exactly.
- `go test` for `scripts/docgenenv`, `scripts/docsmanifestgen`,
`scripts/clidocgen`, and `scripts/apidocgen/postprocess`.
The new tests cover merging, includes, every validation error,
idempotent formatting that keeps comments, and a check that the key
order matches the struct field order.
- `golangci-lint` on the changed packages, and the full `make
pre-commit` hook (gen, fmt, lint including `lint/docs-manifest`, and the
slim build).

## Notes

- Open PRs that edit `docs/manifest.json` will need to redo their
sidebar changes in the YAML sources after this merges.
- #30161 (merged) stopped escaping `&`, `<`, and `>` in
`docs/manifest.json`.
This PR carries that over: all manifest JSON escaping is in
`docgenenv.MarshalJSON`, which sets `enc.SetEscapeHTML(false)`, and
`Manifest.Marshal` from #30161 is gone because the generators no longer
write the manifest.
- #30129 (merged) flattened `admin/external-auth/index.md` and
`ai-coder/agents/tools/index.md` to single files, and the YAML sources
use the new paths.
- The CLI index and REST API page front matter still update only on a
full `make gen` when their sidebar metadata changes, as it did before.
That gap and the overall cost of `make gen` are tracked in
[DOCS-1141](https://linear.app/codercom/issue/DOCS-1141/reduce-the-compute-cost-of-make-gen).

<details>
<summary>Decision log</summary>

### Goal

Make the sidebar easy to reorder and hard to break, with strict key
order and YAML sources, while keeping `docs/manifest.json` as the file
downstream systems read.

### Jsonnet vs. a custom tool

Jsonnet was evaluated first and rejected, based on tests against
go-jsonnet v0.22.0:

- Jsonnet always sorts object keys alphabetically, and there's no option
to keep them in order (`preserve_order` isn't a parameter of
`manifestJsonEx` or `objectFieldsEx`).
Passing today's manifest through Jsonnet and then biome changed 2,114 of
2,478 lines.
- Unknown keys pass through silently, so a `titel:` typo ends up in the
output.
- The sources would be `.jsonnet` files, not YAML.
Reading YAML takes `std.parseYaml(importstr ...)`, and the keys still
come out sorted.
- Restoring key order would need a Go step after Jsonnet anyway, and
that step is most of this tool.

Decoding YAML into Go structs with yaml.v3 (already a dependency) gives
key order from the struct, rejects unknown keys, keeps comments, and
reproduced the current manifest byte for byte.

### Design choices

- **Ordered by `index.yml`:** reordering sections touches only the
index.
- **One route and its direct children per file:** every file stays short
and flat, so a subsection can be found, reviewed, and moved as one file.
Files nest by parent file name rather than by docs path, so the
directory tree matches the sidebar tree.
Include paths are relative to `docs/manifest/`, like `children_from`, so
every reference to a file is spelled the same way.
A child with `children_from` also gets its own file, so the rule has no
exceptions.
- **Generated subtrees in their own fragments:** the generators can't
safely rewrite hand-edited YAML, so they own separate files.
Hand-written metadata for generated routes lives in the YAML, attached
by title, so descriptions never live in a generated file.
- **Key order enforced in both sources and output:** `fmt` rewrites the
sources, `check` fails on unformatted sources, and the output order
comes from the `docgenenv.Route` field order.
- **`&` left unescaped:** the output matches `main` after #30161, which
stopped escaping `&` downstream.
- **`gen/docs-manifest` separate from `fmt`:** keeping manifest assembly
out of `fmt` means `make -j gen fmt` never has two jobs writing
`docs/manifest.json` at once.
- **YAML pitfalls:** unquoted `no` and `123` stay strings because the
fields are typed as strings, an unquoted `: ` is a parse error, and the
silent ` #` truncation is rejected by the comment check.

### Out of scope

- Replacing `&` with "and" in titles (a style question).
- Reducing the cost of `make gen` (DOCS-1141).

</details>

---

_This PR was generated by Coder Agents on behalf of @nickvigilante._
2026-09-30 21:27:14 +00:00
Nick Vigilante 1fd383e4fd docs: flatten lone index pages to single files (#30129)
The beta docs engine turns a directory that holds only an `index.md`
into an empty collapsible section in the sidebar. Two pages on `main`
have that shape: `docs/admin/external-auth/index.md` and
`docs/ai-coder/agents/tools/index.md`. docs-engine is handling the shape
in its own code (the sidebar render fix in
https://github.com/coder/docs-engine/pull/196 and the sync-side handling
in https://github.com/coder/docs-engine/pull/195), and this change
removes it at the source by moving each page to a flat file:
`docs/admin/external-auth.md` and `docs/ai-coder/agents/tools.md`. Both
directories held only `index.md`, with no images or other files.

The change updates the two `docs/manifest.json` paths, 29 inbound
relative links across 17 pages, and the relative links inside the moved
pages (three images and two page links in `external-auth.md`, one page
link in `tools.md`), which now resolve one directory up. The ESR upgrade
table in `docs/install/operate/upgrade/esr-2.29-2.34-upgrade.md` is
re-padded because its link cells shrank, which is why that file shows 28
changed lines. No generator under `scripts/`, the docs-preview workflow,
or `docs/.style` references either path.

Published URLs stay the same, so no redirect is needed. coder.com's
`convertPathToRoute` strips the `index.md` or `.md` suffix, the
docs-preview workflow's `map_doc_path` maps `<dir>/index.md` and
`<dir>.md` to the same route, and docs-engine's `mapMdPath` returns
`admin/external-auth` and `ai-coder/agents/tools` for both the old and
new paths.

For verification, `docs/manifest.json` parses and all 471 of its paths
exist. `make pre-commit-light` passes, which covers markdownlint, the
table formatter, typos, emdash, and shellcheck, and `go run
./scripts/docshtmlcheck` passes. A local script resolved all 306
relative links in the changed files, and every one that the move touched
points at an existing file and heading. linkspector runs on this PR in
CI. A backport PR for `release/2.37` moves the same two pages plus
`admin/licensing` and `install/other`:
https://github.com/coder/coder/pull/30130.

This change was prepared with AI assistance. The AI agent that prepared
it ran the checks described above.

---

🤖 Built with AI assistance.
2026-09-30 14:34:33 -04:00
Nick VigilanteandIan Evans c2d243891f docs: remove feature states from page titles (#30109)
## Summary

Remove feature-state suffixes from page titles and H1s, keeping
availability states in `docs/manifest.json` navigation metadata.

## Changes

- Remove Premium and Beta suffixes from eight page titles.
- Update the docs content guidelines and doc-check skill to require
state metadata instead of title suffixes.
- Preserve the existing `premium` and `beta` state values in
`docs/manifest.json`.

## Testing

- `pnpm check-docs` passed.
- Verified all eight affected manifest entries retain their feature
state and no affected title retains a state suffix.

Refs DOCS-1126.

This pull request was generated by Coder Agents.

---------

Co-authored-by: Ian Evans <5760366+ianjevans@users.noreply.github.com>
2026-09-29 20:13:02 -04:00
Nick Vigilante d40e24cf7d docs: nest the CLI reference by subcommand (#29956)
## Summary

Nests the CLI reference by subcommand so the Command Line sidebar
collapses from 156 flat entries to 49 top-level verbs.
Each page's URL follows the invocation, its page title is the full
command as inline code, and its sidebar label is only its own verb as
inline code.

| | Before | After |
| --- | --- | --- |
| File | `docs/reference/cli/oauth2-provider_dcr_disable.md` |
`docs/reference/cli/oauth2-provider/dcr/disable.md` |
| URL | `/reference/cli/oauth2-provider_dcr_disable` |
`/reference/cli/oauth2-provider/dcr/disable` |
| Page title (front matter) | `oauth2-provider dcr disable` | `` `coder
oauth2-provider dcr disable` `` |
| Sidebar label (manifest) | `oauth2-provider dcr disable` | ``
`disable` `` |

Resolves DOCS-140 (coder/coder half). Also fixes the Makefile part of
DOCS-1073 / DOCS-912, which this change depends on.

## Changes

- `scripts/clidocgen`: pages nest by command path. A command with
visible subcommands becomes its directory's `index.md`, so
`oauth2-provider/index.md` and `oauth2-provider/dcr/index.md` sit above
`oauth2-provider/dcr/disable.md`. Subcommand table links are relative to
the parent page.
- `scripts/clidocgen`: the manifest "Command Line" section is built from
the command tree (nested, sorted by name, hidden commands skipped)
instead of a flat list. The redundant `coder` child that pointed back at
`reference/cli/index.md` is gone.
- `Makefile`: the CLI rule now replaces `docs/reference/cli/` wholesale,
so stale pages get removed. The manifest rule now runs `clidocgen
-manifest-only` on top of the API staging manifest, so the CLI nav is
generated instead of edited by hand. As a result, the nine pages that
had no nav entry (`ai-gateway*`, `secret enable/disable`, `server
fix-oidc-links`) now have one.
- `scripts/configdocgen` and 48 prose pages: links point at the new
paths (for example `reference/cli/server.md` is now
`reference/cli/server/index.md`).

## How to review/QA this PR

- `docs/reference/cli/**/*` (approximately 190 pages): Each of these
pages are generated automatically. Some pages appear deleted, and some
pages appear new. Every deleted page corresponds to a new page somewhere
else, so no information is being deleted. The docs in this section
should adhere to the following, so a test in the build preview for each
of these items should be satisfactory:
- [ ] Each subcommand of a command gets nested as a child instead of
being displayed as a flat list.
  - [ ] Each parent command is stored in an `index.md` file.
- [ ] The title of each page inside the page should now feature the full
`coder XYZ` command.
- [ ] The title of each page inside the page should now be formatted as
inline code.
- [ ] The title of each page in the sidebar should be the most granular
subcommand of the whole command string (if the page's title is `coder
ai-gateway keys create`, the sidebar label is `create`).
- [ ] The title of each page in the sidebar should now be formatted as
inline code.
  - [ ] The page slug for each page is now the most granular subcommand.
  - [ ] All links now point to the correct page.
- `docs/manifest.json`: The changes here reflect the updated structure
of the `docs/reference/cli` section.
- `docs/**/*.md` outside of `docs/reference/cli/**/*` (approximately 50
pages): The extent of the changes in these pages are just updating the
paths to point to the new page locations.
- [ ] The links on the page all point to the correct new path in
`docs/reference/cli`.
- `scripts/clidocgen` (4 files): This is the Go code responsible for
generating everything inside of `docs/reference/cli/**/*`. A typical
code review here should be satisfactory.
- `scripts/configdocgen` (2 files): these two files just have updates to
the raw `.md` path used by `docs/admin/setup/configuration-reference.md`
and updates the tests to reflect that.
- `Makefile`: This updates the `make` targets for generating the CLI
docs to fit the new structure.

## Merge ordering

Without the renderer PRs deployed, both docs sites show literal
backticks in titles and sidebar labels. Without the redirects, the old
flat URLs return 404.

1. Deploy before merging this PR: coder/docs-engine#176 (beta-docs
renders these titles as inline code) and coder/coder.com#1111 (classic
docs renders them as inline code). Both are safe to ship first, because
titles without backticks render as before.
2. Merge this PR.
3. Merge coder/coder.com#1112 (redirects from the old flat URLs) only
once this content reaches the mainline release branch that serves
unversioned `/docs`.

## Testing

- `go test ./scripts/clidocgen/ ./scripts/configdocgen/
./scripts/docshtmlcheck/`: pass. New tests cover page paths, subcommand
links, the nested manifest, and front matter quoting.
- `make docs/reference/cli/index.md docs/manifest.json
docs/admin/setup/configuration-reference.md`, then a forced rerun: the
second run produced no diff.
- Checked the manifest: all 164 CLI entries resolve to a file, no page
is missing a nav entry, and everything outside "Command Line" is
byte-identical.
- Relative `.md` links across `docs/`: 0 broken, same as `main`.
- `make pre-commit` and `make pre-push`: pass. On earlier pre-push
attempts, different unrelated vitest files timed out under host load.
Those files pass when run alone, and the final push passed with
`PARALLEL_JOBS=1`.

<details>
<summary>Implementation plan and decision log</summary>

**Goal (from DOCS-140 and the requester):** URLs follow the command
hierarchy. Pages are titled with the full command, and sidebar entries
with the last verb only. Both are formatted as inline code.

**Contract with the renderers:** a title wrapped entirely in one pair of
backticks (``^`[^`]+`$``) renders as inline code. Plain-text consumers
(`<title>`, meta, search, llms corpus) strip the backticks. Page title
comes from front matter `title`, and the sidebar label from the manifest
node `title`.

**Decisions**

- A parent command is `dir/index.md` rather than a sibling `dir.md`. The
root CLI page is already `index.md`, and both docs-engine and the legacy
coder.com renderer map `x/index.md` to `/x`.
- The manifest is built from the command tree, not from the written
files, so ordering is deterministic and the nesting matches the pages
exactly.
- The manifest discard bug (DOCS-1073 item 1) is fixed here, because
without that fix the generated nested manifest would never reach the
tree. DOCS-1073 items 2 through 4 (a prose fix and a hidden-command
policy) are out of scope.
- `-manifest-only` keeps the existing parallel `make gen` layout: the
API rule still owns `_gen/manifest-staging.json`, and CLI regeneration
doesn't retrigger swag.
- The renderer and redirect work lives in the companion PRs in
coder/docs-engine and coder/coder.com. It is linked above and not
duplicated here.

</details>

---

> [!NOTE]
> Generated by Coder Agents on behalf of @nickvigilante.
2026-09-28 15:44:10 -04:00
Nick Vigilante d43dd98baf docs: rewrite gerund-leading headings and page titles (#30049)
Rewrites the headings and page titles that `Coder.GerundHeading` flags
as gerund-leading, using the imperative for tasks and the noun form for
concepts.
This PR covers only changes that don't rename a page slug.

## Changes

- Rewrites 121 in-page headings, bringing `Coder.GerundHeading` findings
in `docs/` to zero.
- Adds an empty `<a id="old-slug"></a>` above each renamed heading, so
existing bookmarks and inbound deep links still land on the section.
The legacy IDs were computed with the same slugger the classic renderer
uses (`rehype-slug`), and all 115 that exist on production today match
the live `id` attributes; the other 6 are on pages not published yet.
- Updates the 18 in-docs links that targeted old anchors to use the new
slugs, and the link text that quoted the old heading.
- De-gerunds seven page titles (frontmatter and `manifest.json`) whose
slugs don't change, for example `Upgrading Best Practices` to `Upgrade
best practices`.
- Scopes a Vale exception around the glossary term `Coding agent`, where
"Coding" is an adjective.

## Not in this PR

- **Pages with gerund slugs** (`creating-templates`, `getting-started`,
`working-with-dev-containers`, `cloning-git-repositories`,
`configuring-okta`, and others) keep their current titles, so the title
and slug change together in the slug-rename PR.
  The redirects for those renames come in a third PR.
- **Links outside `docs/`** to old anchors (Go notification strings, an
applied SQL migration, golden files, and the generated `prometheus.md`)
still target old slugs.
They keep working through the legacy anchors, and updating them would
need code changes.

## Known limitation

`coder/docs-engine` prefixes raw-HTML `id` attributes with
`user-content-` (DOM-clobbering protection), and inbound deep links
aren't rewritten.
Once the docs move to docs-engine, these legacy anchors resolve only as
`#user-content-<old-slug>` unless docs-engine adds a fragment fallback.
They work on the current coder.com renderer and on GitHub.

## Testing

- `vale --filter='.Name=="Coder.GerundHeading"' docs`: zero findings,
down from 122.
- `make lint/markdown`, `make lint/docs-html`, `make lint/emdash`, and
the pre-commit hook: all pass.
- Scripted check that every relative `#fragment` link into a changed
file resolves to a heading slug or a legacy anchor (177 links).
- Scripted check that no legacy ID collides with a heading slug on the
same page, and that no untouched heading's duplicate-suffix slug
shifted.

<details>
<summary>Heading renames and legacy anchors (121)</summary>

| File | Old heading | New heading | Legacy anchor |
| --- | --- | --- | --- |
| `admin/integrations/opentofu.md` | Using a custom Terraform binary |
Use a custom Terraform binary | `#using-a-custom-terraform-binary` |
| `admin/monitoring/connection-logs.md` | Capturing/Exporting Connection
Logs | Capture and export connection logs |
`#capturingexporting-connection-logs` |
| `admin/monitoring/metrics.md` | Setting up metrics | Set up metrics |
`#setting-up-metrics` |
| `admin/networking/port-forwarding.md` | Accessing workspace ports |
Access workspace ports | `#accessing-workspace-ports` |
| `admin/networking/port-forwarding.md` | Sharing ports | Share ports |
`#sharing-ports` |
| `admin/networking/port-forwarding.md` | Configuring port protocol |
Configure port protocol | `#configuring-port-protocol` |
| `admin/networking/workspace-proxies.md` | Running on Kubernetes | Run
on Kubernetes | `#running-on-kubernetes` |
| `admin/networking/workspace-proxies.md` | Running on a VM | Run on a
VM | `#running-on-a-vm` |
| `admin/networking/workspace-proxies.md` | Running as a system service
| Run as a system service | `#running-as-a-system-service` |
| `admin/networking/workspace-proxies.md` | Running in Docker | Run in
Docker | `#running-in-docker` |
| `admin/networking/workspace-proxies.md` | Selecting a proxy | Select a
proxy | `#selecting-a-proxy` |
| `admin/security/audit-logs.md` | Capturing/Exporting Audit Logs |
Capture and export audit logs | `#capturingexporting-audit-logs` |
| `admin/security/audit-logs.md` | Purging Old Audit Logs | Purge old
audit logs | `#purging-old-audit-logs` |
| `admin/security/database-encryption.md` | Enabling encryption | Turn
on encryption | `#enabling-encryption` |
| `admin/security/database-encryption.md` | Rotating keys | Rotate keys
| `#rotating-keys` |
| `admin/security/database-encryption.md` | Disabling encryption | Turn
off encryption | `#disabling-encryption` |
| `admin/security/database-encryption.md` | Deleting Encrypted Data |
Delete encrypted data | `#deleting-encrypted-data` |
| `admin/security/secrets.md` | Displaying Secrets | Display secrets |
`#displaying-secrets` |
| `admin/setup/data-retention.md` | Keeping Data Indefinitely | Keep
data indefinitely | `#keeping-data-indefinitely` |
| `admin/setup/index.md` | Migrating from the built-in database to an
external database | Migrate from the built-in database to an external
database |
`#migrating-from-the-built-in-database-to-an-external-database` |
| `admin/setup/index.md` | Configuring Coder behind a proxy | Configure
Coder behind a proxy | `#configuring-coder-behind-a-proxy` |
| `admin/setup/telemetry.md` | Toggling | Turn telemetry on or off |
`#toggling` |
| `admin/templates/creating-templates.md` | Disabling the template
builder | Turn off the template builder |
`#disabling-the-template-builder` |
| `admin/templates/extending-templates/agent-metadata.md` | Managing the
database load | Manage the database load | `#managing-the-database-load`
|
| `admin/templates/extending-templates/docker-in-workspaces.md` |
Authenticating with a Private Registry | Authenticate with a private
registry | `#authenticating-with-a-private-registry` |
| `admin/templates/extending-templates/index.md` | Running scripts on
workspace lifecycle | Run scripts on workspace lifecycle events |
`#running-scripts-on-workspace-lifecycle` |
| `admin/templates/extending-templates/parameters.md` | Validating
parameters | Validate parameters | `#validating-parameters` |
| `admin/templates/extending-templates/prebuilt-workspaces.md` |
Invalidating prebuilds | Invalidate prebuilds |
`#invalidating-prebuilds` |
| `admin/templates/extending-templates/prebuilt-workspaces.md` |
Managing resource quotas | Manage resource quotas |
`#managing-resource-quotas` |
| `admin/templates/extending-templates/prebuilt-workspaces.md` |
Managing prebuild provisioning queues | Manage prebuild provisioning
queues | `#managing-prebuild-provisioning-queues` |
| `admin/templates/extending-templates/prebuilt-workspaces.md` |
Preventing resource replacement | Prevent resource replacement |
`#preventing-resource-replacement` |
| `admin/templates/extending-templates/prebuilt-workspaces.md` |
Preventing prebuild queue contention (recommended) | Prevent prebuild
queue contention (recommended) |
`#preventing-prebuild-queue-contention-recommended` |
| `admin/templates/extending-templates/process-logging.md` | Configuring
custom templates to use workspace process logging | Configure custom
templates to use workspace process logging |
`#configuring-custom-templates-to-use-workspace-process-logging` |
| `admin/templates/extending-templates/process-logging.md` | Viewing
workspace process logs | View workspace process logs |
`#viewing-workspace-process-logs` |
| `admin/templates/extending-templates/process-priority.md` | Verifying
the feature is enabled | Verify the feature is enabled |
`#verifying-the-feature-is-enabled` |
| `admin/templates/extending-templates/resource-metadata.md` | Hiding
resources in the dashboard | Hide resources in the dashboard |
`#hiding-resources-in-the-dashboard` |
| `admin/templates/extending-templates/resource-metadata.md` | Using a
custom resource icon | Use a custom resource icon |
`#using-a-custom-resource-icon` |
| `admin/templates/extending-templates/resource-ordering.md` | Using
"order" property | Use the "order" property | `#using-order-property` |
| `admin/templates/extending-templates/resource-persistence.md` |
Disabling persistence | Turn off persistence | `#disabling-persistence`
|
| `admin/templates/index.md` | Extending templates | Extend templates |
`#extending-templates` |
| `admin/templates/managing-templates/change-management.md` | Testing
and Publishing Coder Templates in CI/CD | Test and publish Coder
templates in CI/CD | `#testing-and-publishing-coder-templates-in-cicd` |
| `admin/templates/managing-templates/index.md` | Creating templates |
Create templates | `#creating-templates` |
| `admin/templates/managing-templates/index.md` | Editing templates |
Edit templates | `#editing-templates` |
| `admin/templates/managing-templates/index.md` | Updating templates |
Update templates | `#updating-templates` |
| `admin/templates/troubleshooting.md` | Debugging the startup script |
Debug the startup script | `#debugging-the-startup-script` |
| `admin/users/groups-roles.md` | Creating custom roles | Create custom
roles | `#creating-custom-roles` |
| `admin/users/index.md` | Configuring SSO | Configure SSO |
`#configuring-sso` |
| `admin/users/index.md` | Resetting a password on Kubernetes | Reset a
password on Kubernetes | `#resetting-a-password-on-kubernetes` |
| `admin/users/quotas.md` | Establishing Costs | Establish costs |
`#establishing-costs` |
| `admin/users/quotas.md` | Establishing Budgets | Establish budgets |
`#establishing-budgets` |
| `admin/users/sessions-tokens.md` | Creating tokens with scopes |
Create tokens with scopes | `#creating-tokens-with-scopes` |
| `ai-coder/agent-firewall/index.md` | Getting Started with Agent
Firewall | Get started with Agent Firewall |
`#getting-started-with-agent-firewall` |
| `ai-coder/agent-firewall/index.md` | Viewing Audit Logs | View audit
logs | `#viewing-audit-logs` |
| `ai-coder/agent-firewall/nsjail/k8s.md` | Running without user
namespaces | Run without user namespaces |
`#running-without-user-namespaces` |
| `ai-coder/agent-firewall/version.md` | Using Coder Before v2.30.0 with
Claude Code Module v4.7.0+ | Coder before v2.30.0 with Claude Code
module v4.7.0+ |
`#using-coder-before-v2300-with-claude-code-module-v470` |
| `ai-coder/agent-firewall/version.md` | Using Claude Code Module Before
v4.7.0 | Claude Code module before v4.7.0 |
`#using-claude-code-module-before-v470` |
| `ai-coder/agents/getting-started.md` | Providing feedback | Provide
feedback | `#providing-feedback` |
| `ai-coder/agents/models.md` | Managing personal API keys | Manage
personal API keys | `#managing-personal-api-keys` |
| `ai-coder/agents/models.md` | Removing a personal key | Remove a
personal key | `#removing-a-personal-key` |
| `ai-coder/agents/models.md` | Using an LLM proxy | Use an LLM proxy |
`#using-an-llm-proxy` |
| `ai-coder/agents/platform-controls/git-providers.md` | Checking logs |
Check logs | `#checking-logs` |
| `ai-coder/ai-gateway/audit.md` | Navigating the UI | Navigate the UI |
`#navigating-the-ui` |
| `ai-coder/ai-gateway/audit.md` | Conducting a forensic audit | Conduct
a forensic audit | `#conducting-a-forensic-audit` |
| `ai-coder/ai-gateway/clients/jetbrains.md` | Using the AI Assistant |
Use the AI Assistant | `#using-the-ai-assistant` |
| `install/operate/upgrade/best-practices.md` | Diagnosing liveness
probe issues | Diagnose liveness probe issues |
`#diagnosing-liveness-probe-issues` |
| `install/operate/upgrade/best-practices.md` | Recovering from failed
database migrations | Recover from failed database migrations |
`#recovering-from-failed-database-migrations` |
| `install/operate/upgrade/esr-2.24-2.29-upgrade.md` | Upgrading |
Upgrade recommendations | `#upgrading` |
| `install/operate/upgrade/esr-2.29-2.34-upgrade.md` | Upgrading |
Upgrade recommendations | `#upgrading` |
| `install/prepare/licensing.md` | Adding your license key | Add your
license key | `#adding-your-license-key` |
| `install/server/cloud/azure-vm.md` | Creating and Uploading Your First
Template | Create and upload your first template |
`#creating-and-uploading-your-first-template` |
| `install/server/cloud/compute-engine.md` | Configuring the control
plane | Configure the control plane | `#configuring-the-control-plane` |
| `reference/releases.md` | Installing stable | Install stable |
`#installing-stable` |
| `start/first-workspace.md` | Creating your first coder workspace |
Create your first Coder workspace |
`#creating-your-first-coder-workspace` |
| `start/local-deploy.md` | Setting up a Coder deployment | Set up a
Coder deployment | `#setting-up-a-coder-deployment` |
| `tutorials/best-practices/organizations.md` | Managing Organizations
at Scale | Manage organizations at scale |
`#managing-organizations-at-scale` |
| `tutorials/example-guide.md` | Adding Photos | Add photos |
`#adding-photos` |
| `tutorials/example-guide.md` | Setting the author data | Set the
author data | `#setting-the-author-data` |
| `tutorials/example-guide.md` | Setting up the routes | Set up the
routes | `#setting-up-the-routes` |
| `tutorials/persistent-shared-workspaces.md` | Finding shared
workspaces | Find shared workspaces | `#finding-shared-workspaces` |
| `tutorials/reverse-proxy-caddy.md` | Getting started | Get started |
`#getting-started` |
| `tutorials/reverse-proxy-caddy.md` | Generating wildcard certificates
| Generate wildcard certificates | `#generating-wildcard-certificates` |
| `tutorials/reverse-proxy-nginx.md` | Adding Coder deployment subdomain
| Add the Coder deployment subdomain |
`#adding-coder-deployment-subdomain` |
| `tutorials/testing-templates.md` | Creating the headless user | Create
the headless user | `#creating-the-headless-user` |
| `user-guides/desktop/desktop-connect-sync.md` | Accessing web apps in
a secure browser context | Access web apps in a secure browser context |
`#accessing-web-apps-in-a-secure-browser-context` |
| `user-guides/desktop/index.md` | Testing Your Connection | Test your
connection | `#testing-your-connection` |
| `user-guides/desktop/index.md` | Getting Help | Get help |
`#getting-help` |
| `user-guides/desktop/index.md` | Uninstalling | Uninstall Coder
Desktop | `#uninstalling` |
| `user-guides/devcontainers/index.md` | Getting started | Get started |
`#getting-started` |
| `user-guides/devcontainers/troubleshooting-dev-containers.md` |
Finding your dev container agent | Find your dev container agent |
`#finding-your-dev-container-agent` |
| `user-guides/devcontainers/troubleshooting-dev-containers.md` |
Getting more help | Get more help | `#getting-more-help` |
| `user-guides/devcontainers/working-with-dev-containers.md` | Using the
Coder CLI | Use the Coder CLI | `#using-the-coder-cli` |
| `user-guides/devcontainers/working-with-dev-containers.md` | Using
OpenSSH (config-ssh) | Use OpenSSH (config-ssh) |
`#using-openssh-config-ssh` |
| `user-guides/devcontainers/working-with-dev-containers.md` | Exposing
ports on the parent workspace | Expose ports on the parent workspace |
`#exposing-ports-on-the-parent-workspace` |
| `user-guides/devcontainers/working-with-dev-containers.md` |
Rebuilding dev containers | Rebuild dev containers |
`#rebuilding-dev-containers` |
| `user-guides/shared-workspaces.md` | Getting Started | Get started |
`#getting-started` |
| `user-guides/shared-workspaces.md` | Sharing your Workspace | Share
your workspace | `#sharing-your-workspace` |
| `user-guides/shared-workspaces.md` | Using a shared workspace | Use a
shared workspace | `#using-a-shared-workspace` |
| `user-guides/shared-workspaces.md` | Accessing workspace apps in
shared workspaces | Access workspace apps in shared workspaces |
`#accessing-workspace-apps-in-shared-workspaces` |
| `user-guides/workspace-access/emacs-tramp.md` | Connecting To A
Workspace | Connect to a workspace | `#connecting-to-a-workspace` |
| `user-guides/workspace-access/emacs-tramp.md` | Using SSH | Use SSH |
`#using-ssh` |
| `user-guides/workspace-access/index.md` | Running remote commands with
quoting | Run remote commands with quoting |
`#running-remote-commands-with-quoting` |
| `user-guides/workspace-access/jetbrains/gateway.md` | Configuring the
Gateway plugin to use internal certificates | Configure the Gateway
plugin to use internal certificates |
`#configuring-the-gateway-plugin-to-use-internal-certificates` |
| `user-guides/workspace-access/jetbrains/gateway.md` | Using an
existing JetBrains installation in the workspace | Use an existing
JetBrains installation in the workspace |
`#using-an-existing-jetbrains-installation-in-the-workspace` |
| `user-guides/workspace-access/port-forwarding.md` | Accessing
workspace ports | Access workspace ports | `#accessing-workspace-ports`
|
| `user-guides/workspace-access/port-forwarding.md` | Sharing ports |
Share ports | `#sharing-ports` |
| `user-guides/workspace-access/port-forwarding.md` | Configuring port
protocol | Configure port protocol | `#configuring-port-protocol` |
| `user-guides/workspace-access/vscode.md` | Using the public extensions
marketplaces | Use the public extensions marketplaces |
`#using-the-public-extensions-marketplaces` |
| `user-guides/workspace-access/vscode.md` | Adding extensions to custom
images | Add extensions to custom images |
`#adding-extensions-to-custom-images` |
| `user-guides/workspace-access/vscode.md` | Installing extensions using
its `vsix` file at the command line | Install extensions using a `vsix`
file at the command line |
`#installing-extensions-using-its-vsix-file-at-the-command-line` |
| `user-guides/workspace-access/vscode.md` | Installing from a
marketplace at the command line | Install from a marketplace at the
command line | `#installing-from-a-marketplace-at-the-command-line` |
| `user-guides/workspace-access/vscode.md` | Using VS Code Desktop | Use
VS Code Desktop | `#using-vs-code-desktop` |
| `user-guides/workspace-access/web-terminal.md` | Accessing the
Terminal | Access the terminal | `#accessing-the-terminal` |
| `user-guides/workspace-lifecycle.md` | Stopping workspaces | Stop
workspaces | `#stopping-workspaces` |
| `user-guides/workspace-lifecycle.md` | Deleting workspaces | Delete
workspaces | `#deleting-workspaces` |
| `user-guides/workspace-management.md` | Creating workspaces | Create
workspaces | `#creating-workspaces` |
| `user-guides/workspace-management.md` | Updating workspaces | Update
workspaces | `#updating-workspaces` |
| `user-guides/workspace-management.md` | Updating via the CLI | Update
with the CLI | `#updating-via-the-cli` |
| `user-guides/workspace-management.md` | Starting and stopping
workspaces | Start and stop workspaces |
`#starting-and-stopping-workspaces` |
| `user-guides/workspace-management.md` | Repairing workspaces | Repair
workspaces | `#repairing-workspaces` |
| `user-guides/workspace-scheduling.md` | Working hours | Work schedule
| `#working-hours` |
| `user-guides/workspace-scheduling.md` | Basing solely on activity
detection | Rely solely on activity detection |
`#basing-solely-on-activity-detection` |

</details>

<details>
<summary>Plan</summary>

The de-gerund work ships as three PRs:

1. This PR: in-page headings and page titles that do not require a page
slug rename.
1. Page slug renames for pages whose filenames are gerunds.
1. Redirects for the renamed slugs.

Heading rewrites follow the style guide section "No gerund-leading
headings": imperative for task headings, noun form for concept headings,
sentence case, and "turn on"/"turn off" instead of "enable"/"disable"
per word-choice guidance.
The legacy anchor sits on its own line above the heading (with a blank
line between) rather than inside the heading, so it does not leak into
heading text, TOC entries, or Fumadocs slugs.

</details>

---

_Generated by Coder Agents on behalf of @nickvigilante._
2026-09-28 13:40:07 -04:00
Nick Vigilante 88aa2cc373 feat: enable Coder.UnitSpacing (#29944)
## Summary

Adds `Coder.UnitSpacing`, a Vale rule that enforces the style guide's
[non-breaking space between number and
unit](https://github.com/coder/coder/blob/main/docs/.style/style-guide/numbers-units-and-dates.md#non-breaking-space-between-number-and-unit)
policy.
Until now, the guide pointed at `Google.Units (planned)`, but that rule
accepts a regular space, so it can't enforce the policy.

Resolves [DOCS-1072](https://linear.app/codercom/issue/DOCS-1072).

## Changes

- **Cleanup commit**: replaces the regular space (or missing space) with
`&nbsp;` in 189 prose findings across 55 files. One config default,
`(default: 15s)`, moves to code font instead, as the guide requires for
configuration values. One table row that was realigned also swaps a
pre-existing en dash for a hyphen, since `lint/emdash` checks changed
lines.
- **Enable commit**: adds `docs/.style/styles/Coder/UnitSpacing.yml` at
`warning`, updates the enforcement footer in
`numbers-units-and-dates.md`, and updates the coverage tables in the
style guide README.
- **Reflow commit**: every hard-wrapped paragraph the cleanup touched
(45 paragraphs in 29 files) is rewrapped to one sentence per line.
Joining lines exposed units split across a line break, which now use
`&nbsp;`, and two em dashes on rewritten lines are reworded.
- **Rule fix commit**: the token now matches ASCII whitespace, including
a newline, between the number and the unit, so a hard-wrapped paragraph
can't hide a missing `&nbsp;`. The class is spelled out because Vale's
`\s` is Unicode and would match U+00A0. Vale can't locate a match that
spans list indentation or a blockquote marker, so splits there still go
unreported.
- **Rule count sync**: the tooling paragraph in the style guide README
now reads 79 rules, 9 tool-checked, to match the coverage table. The 79
corrects drift already on `main` since #29758; the 9 is this PR's new
rule.
- `.vale.ini` turns the rule off for pages generated from Go source:
`docs/reference/{api,cli}/*.md`, `configuration-reference.md`, and
`prometheus.md`. That text comes from flag descriptions, Swagger
comments, and metric help text, which also render outside Markdown,
where `&nbsp;` would show up literally.

<details>
<summary>Rule design decisions</summary>

- An `existence` rule matches `\d+(\.\d+)?` followed by a regular space
or no space, then a unit from a curated list (time, data size,
bandwidth, frequency, vCPU/CPU/cores).
- Vale renders Markdown before matching, so `&nbsp;` and a literal
U+00A0 both pass. Code spans and fences are skipped.
- Hyphenated compound modifiers (`12-hour clock`) don't match, because
the token allows only a space or nothing between the number and the
unit.
- Exceptions cover HTTP status plurals (`413s`) and decades (`1990s`).
- Severity is `warning`, because the unit list is curated and a count
followed by a unit-like word can still be a judgment call.
- Alternative considered: a `BlockIgnores` pattern for the `<!-- Code
generated ... -->` regions. I didn't use it because it would silence
every rule in those regions, not just this one.

</details>

## Testing

- Ran the rule against fixtures: `30 seconds`, `150ms`, `4 vCPU` in a
table, and `16` / `GB` split across lines in a paragraph all flag.
`30&nbsp;seconds`, literal U+00A0, `12-hour`, `413s`, `1990s`, and ``
`30s` `` don't.
- `vale docs/` reports 0 `Coder.UnitSpacing` findings, and the counts
for every other rule match `main`.
- A scratch config with `Coder.OneSentencePerLine` turned on reports 0
findings on changed lines.
- `make lint/markdown`, `make lint/style-claims`, `make lint/emdash`,
and the `pre-commit-light` hook pass.

<details>
<summary>CI flake note</summary>

`Storybook` failed on b3c53b6874 after capturing all 3,421 snapshots,
when the Pixel build was marked `failed` during finalization. This PR
changes no files under `site/`. The re-run failed the same way, and
`Storybook` is failing in the same Pixel step on the latest `main`
commits, so this isn't caused by this PR.

</details>

---

*Generated by [Coder Agents](https://coder.com/docs/ai-coder/agents) on
behalf of @nickvigilante.*
2026-09-28 12:29:40 -04:00
Michael Suchacz 7c74fa3548 feat: add workspace file upload agent API (#27078)
## What

Adds a dedicated workspace-agent endpoint, `POST
/api/v0/upload-chat-file`, that streams a request body into the chat's
upload directory `~/.coder/chats/<chat-id>/files/<name>`, plus the SDK
plumbing to call it:

- `coderd/x/chatfiles`: upload-name sanitization
(`SanitizeWorkspaceUploadName`), collision suffixes (compound extensions
such as `.tar.gz` stay intact: `archive_2.tar.gz`), and the shared
`~/.coder/chats/<id>/files` path layout.
- `agent/agentfiles`: the HTTP handler. On Linux/macOS the directory
chain is opened with `openat(O_NOFOLLOW)`, so symlinked path components
are rejected. The body streams into a hidden `.<name>.upload-<random>`
temp file and is published under its final name only when complete, via
`linkat` over the `_2`, `_3`, ... collision sequence, so existing files
are never overwritten and a half-written file is never visible under its
final name. Filesystems without hard links fall back to an exclusive
placeholder plus `renameat`. Other platforms use an afero-based fallback
with the same semantics. Stale temp files older than an hour are swept
before each upload, and failures are logged with a structured warning.
- `codersdk/workspacesdk`: `AgentConn.UploadChatFile` + regenerated
`agentconnmock`.

## Why

Coder Agents chat can attach images/text/PDF to prompts, but arbitrary
files (zips, datasets, binaries) have nowhere to land where the agent's
tools can act on them. This is the agent-side foundation; the coderd
route that fronts it is stacked on top.

Design notes:

- There is deliberately no size cap; the resource profile matches
existing SSH/WebSocket proxying, and client cancellation aborts the
stream (a failed write removes the temp file, so no partial files
remain). Because the agent HTTP server uses fixed 20 s read/write
timeouts, the handler pushes both deadlines forward on every received
chunk, so a slow upload only fails after a 20 s stall. If the connection
does not support deadline changes, the agent logs one warning per
request.
- This endpoint is not gated by `CODER_AGENT_BLOCK_FILE_TRANSFER`. That
flag targets SSH-transport file tools (scp/rsync/sftp). Chat uploads are
an agent API capability like `write-file` and `edit-files`, which
already bypass the flag; the interaction is documented in the FAQ.

## How tested

- `go test ./agent/agentfiles ./coderd/x/chatfiles
./codersdk/workspacesdk` (unit + umask/symlink/collision integration
tests).
- UAT on a dogfood-style deployment (2026-09-21): uploads longer than 20
s went from 502 to 201 once the workspace agent was restarted on this
build; a coderd restart alone keeps the old agent running, so the
per-chunk deadline extension needs the agent rolled (older agents get
the 409 "Restart the workspace" from #27079).

Part of CODAGT-211. Stack: agent API (this PR) -> coderd endpoint -> UI.

> This PR was authored by Mux (AI) working on Mike's behalf.
2026-09-25 09:05:51 +02:00
Nick Vigilante 3d30e201b5 docs: move contributor guides out of the published docs into the repo (#29751)
## Merge order

Merge first; then republish the docs; then coder/coder.com#1100 (the
redirects).

## Summary

The nine pages under `docs/about/contributing/` are for people working
in this repository, not for Coder users or administrators, so they leave
the published documentation.
The repository becomes their canonical home.
Until now the root `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md` were
two-line stubs that only linked to the docs site, so contributors on
GitHub were bounced out to read them.

A companion PR in `coder/coder.com` adds redirects from every retired
`/docs/about/contributing/*` URL to the new GitHub location.
It must merge only after this PR has merged and the docs have
republished.

## Changes

| Old path | New path |
| -- | -- |
| `docs/about/contributing/CONTRIBUTING.md` | `CONTRIBUTING.md`
(replaces the stub) |
| `docs/about/contributing/CODE_OF_CONDUCT.md` | `CODE_OF_CONDUCT.md`
(replaces the stub) |
| `docs/about/contributing/SECURITY.md` | deleted; the root
`SECURITY.md` already holds the policy |
| `docs/about/contributing/AI_CONTRIBUTING.md` |
`contributing/AI_CONTRIBUTING.md` |
| `docs/about/contributing/backend.md` | `contributing/backend.md` |
| `docs/about/contributing/frontend.md` | `contributing/frontend.md` |
| `docs/about/contributing/modules.md` | `contributing/modules.md` |
| `docs/about/contributing/templates.md` | `contributing/templates.md` |
| `docs/about/contributing/documentation.md` |
`contributing/documentation.md` (see note below) |

For every moved file: the docs front matter is stripped and its `title`
becomes the H1, relative links are rewritten for the new depth
(`../../../foo` becomes `./foo` at the root and `../foo` under
`contributing/`; the two links into `docs/admin/` become
`../docs/admin/...`), and the docs-only `<div class="tabs">` wrapper in
`CONTRIBUTING.md` is removed.
`CONTRIBUTING.md` gains a short intro that links the code of conduct,
the security policy and the area guides, since GitHub only auto-surfaces
the root files.

`docs/manifest.json` loses the About → Contributing group and
`docs/images/icons/contributing.svg`, which only that group used.

Inbound references updated:

- `README.md`: contribution guide link now points at `CONTRIBUTING.md`
on GitHub.
- `docs/tutorials/example-guide.md` and
`docs/install/releases/feature-stages.md`: link to
`contributing/documentation.md` on GitHub, since a docs page cannot link
to a repo-only file.
- `docs/.style/style-guide/README.md`: the "Relationship to" section
names the new location.
- `scripts/docshtmlcheck/main.go`: both the package comment and the
user-facing error string name `contributing/documentation.md`.
- `scripts/docshtmlcheck/README.md`: relative link to the new location.
- `coderd/database/migrations/migrate_test.go`: `t.Log` link now points
at `contributing/backend.md#database-fixtures-for-testing-migrations`.
- `.github/pull_request_template.md`: AI contribution guidelines link.
- `.github/workflows/contrib.yaml`: commit-message guidelines link in
the PR title check.
- `.github/workflows/test-docs-preview-mapper.sh`: the mapper test case
that used `docs/about/contributing/CONTRIBUTING.md` now uses
`docs/admin/security/audit-logs.md`.
-
`.claude/docs/{ARCHITECTURE,PR_STYLE_GUIDE,WORKFLOWS,DOCS_STYLE_GUIDE}.md`:
links to `CONTRIBUTING.md`.

`git grep about/contributing` is empty after this change.
`CODEOWNERS` had no entry for the old paths.

Note on `documentation.md`: it is docs-authoring guidance and is parked
in `contributing/` for now.
Folding it into `docs/.style/` is tracked separately and is deliberately
not attempted here.

## Testing

- `go run ./scripts/docshtmlcheck` over `docs/`, and again over
`contributing/`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md`: clean.
- `go vet ./scripts/docshtmlcheck/...` and `go test
./scripts/docshtmlcheck/...`: 47 tests pass.
- `go vet ./coderd/database/migrations/...`: clean. `go test` for that
package fails to link on this machine (`tailscale.com/tstime/mono:
invalid reference to runtime.nanotime1`) identically on unmodified
`main`, so it is a local toolchain issue and not exercised here; the
change is a string in a `t.Log`.
- `pnpm check-docs` (markdownlint-cli2 and markdown-table-formatter over
`docs/`, `.claude/docs/`, root `*.md`): 495 files, 0 errors. The same
two tools over `contributing/*.md`: 0 errors.
- Every relative link in the eight moved files resolves to an existing
path (scripted check).
- `bash .github/workflows/test-docs-preview-mapper.sh`: the changed
`assert_maps_to` case passes; the six `resolve_ref`/`pages_for_image`
failures it reports are identical on unmodified `main` on macOS.

Linear: [DOCS-975](https://linear.app/codercom/issue/DOCS-975)

---

🤖 Built with AI assistance.
2026-09-24 19:42:18 +00:00
Nick Vigilante d5a6047879 docs: restructure Install into the five phase deployment spine (#29304)
Restructures the Install section into the five-phase deployment spine,
so the reading order matches the order a deployment actually happens:
**plan, prepare, install, validate, operate.** Today the first thing a
reader sees is a shell command, and architecture and sizing sit
downstream in Administration, after the point where they would have been
useful.

Resolves [DOCS-869](https://linear.app/codercom/issue/DOCS-869). Part of
[DOCS-685](https://linear.app/codercom/issue/DOCS-685) and
[DOCS-738](https://linear.app/codercom/issue/DOCS-738).

> [!IMPORTANT]
> **Stacked on [#28960](https://github.com/coder/coder/pull/28960)**
(DOCS-871, terminology). Base is that branch, not `main`. Retarget to
`main` after it merges.
>
> **Paired with coder/coder.com
[#1077](https://github.com/coder/coder.com/pull/1077)**, which must
merge **first**. Without it, every inbound link to a moved page 404s in
the gap.

## Deliberately mechanical

No prose changes. The only edits inside moved pages are relative link
and image path repairs, so the diff reads as renames. That is the point:
this is the highest link-breakage change in the project, and it should
be reviewable as such.

The prose work is [DOCS-868](https://linear.app/codercom/issue/DOCS-868)
(phase overview, architecture rewrite, sizing split, actor table) and
[DOCS-872](https://linear.app/codercom/issue/DOCS-872) (new pages).

## The new spine

```text
Install and deploy Coder            /install
├── Coder CLI                                   [unchanged, retired by DOCS-870]
├── 1. Plan your deployment
│   ├── Architecture and components             [from admin/infrastructure]
│   └── Coder Validated Architecture            [from admin/infrastructure]
│       └── 1k / 2k / 3k / 10k users
├── 2. Prepare prerequisites
│   ├── Licensing                               [from admin/licensing]
│   ├── Air-gapped deployments                  [from install/airgap]
│   └── Mirror images and registries            [from install/registry-mirror-artifactory]
├── 3. Install the control plane
│   ├── Docker / Kubernetes / Rancher / OpenShift
│   ├── Cloud marketplace and VM images
│   └── Unofficial install methods
├── 4. Validate your deployment
│   └── Scale testing                           [from admin/infrastructure]
│       ├── Scale test utility
│       └── Scaling best practices
└── 5. Operate and maintain
    ├── External provisioners                   [from admin/provisioners]
    ├── Upgrading (+ best practices, ESR guides)
    └── Uninstall
```

Leaving Install: release channels become `reference/releases`, feature
stages become `reference/feature-stages`.

`docs/admin/infrastructure/` is gone entirely, which is what DOCS-738
recommended.

## Numbers

- 31 pages moved, all as `git mv` renames.
- 1 page deleted: `admin/infrastructure/index.md`, whose content was a
one-line section blurb.
- 5 new placeholder index pages, one per phase, each a sentence of
orientation plus `<children></children>`. The nav schema requires a path
on every entry, so a phase cannot exist without one.
- 265 relative links and asset paths repaired across 57 files.
- `docs/manifest.json` restructured. Entry count goes 460 to 464: plus 5
stubs, minus the deleted Infrastructure entry.

## Reviewer attention, please

**The redirects PR repairs 19 existing redirects, not just adds new
ones.** This was the surprise. `redirects.json` already had entries
whose *destinations* pointed at pages this PR moves, so they would have
started redirecting to 404s. Examples:
`/docs/architecture/architecture`, `/docs/admin/architectures`,
`/docs/scaling/scale-testing`, `/docs/licensing`, `/docs/enterprise`,
`/docs/install/offline`, `/docs/platforms`, `/docs/install/1-click`,
`/docs/admin/upgrade`, `/docs/install/cloud/ec2`, and both
`feature-stages` aliases. All 19 are fixed in the paired PR.

**This PR touches `scripts/`, so it is not docs-only.** Two release
tools write to paths that moved and would have broken silently at
release time:

- `scripts/update-release-calendar.sh`: `DOCS_FILE` now points at
`docs/reference/releases.md`.
- `scripts/releaser/docs.go`: `releasesFile`, and also `kubernetesFile`
and `rancherFile`, which pointed at `docs/install/kubernetes.md` and
`docs/install/rancher.md`.

<details>
<summary>Deliberate scope decisions</summary>

- **Self-managed vs cloud grouping is flattened.** The reconciled IA
nested platform pages under a "Self-managed platforms" parent. That
would need two more placeholder index pages for groups with no content
yet, so the platform pages sit directly under Phase 3 and the grouping
lands with the DOCS-868 prose.
- **Release-notes URLs left alone.** `scripts/releaser/release.go` and
`scripts/release/generate_release_notes.sh` embed
`https://coder.com/docs/install/releases` and `/docs/install/upgrade` in
release-note templates. The redirects cover them, and changing the
strings would require updating Go test fixtures in
`scripts/release/main_internal_test.go`. Better as its own change.
- **In-product doc links left alone.** Several `site/src` pages link to
`coder.com/docs/admin/provisioners` and similar. Those are absolute
URLs, so the redirects cover them. Updating them is a frontend change
that would pull frontend review into a docs restructure.
- **`admin/security/audit-logs.md` line 237** is hand-written prose
below the generated table, and its `../licensing/index.md` link had to
move with the licensing page. Verified `make gen` does not revert it.
- **`reference/feature-stages.md`** has generated blocks whose relative
links changed depth with the move. The generator named in its own marker
comment (`scripts/release/docs_update_feature_stages.sh`) no longer
exists in the repo, so those links were repaired by hand and the file is
effectively hand-maintained. Worth its own issue.
- **`install/cli.md` keeps its nav slot** here. DOCS-870 retires it.

</details>

## Verification

| Check | Result |
|---|---|
| Broken relative link scan | 0 in `install/`, `admin/`, `reference/`.
21 remain, all pre-existing illustrative examples in
`docs/.style/style-guide/**` and one malformed URL in `docs/README.md` |
| `make gen` | clean, manifest not clobbered, phase structure intact |
| `make lint/markdown` | pass, 508 files, 0 errors |
| `make lint/emdash` | pass |
| `make lint/docs-html` | pass |
| `make lint/prose` | pass, 1 intentional demo error, no new findings |
| `make pre-commit` | pass, including `lint/go`, `lint/ts`,
`lint/shellcheck`, and a slim build |
| Manifest audit | every path resolves to a file on disk; entry-by-entry
diff shows only the expected moves |

Generated with Coder Agents on behalf of @nickvigilante.


<details>
<summary>CI note: doc-check failure is infrastructure, not this
PR</summary>

`Analyze PR for Documentation Updates Needed` fails before it reviews
anything: the Coder account behind `agents-chat-action` returns `User is
not active (status = "suspended")`. It isn't a required check.
`check-docs` (linkspector) and every other check pass on `58ed9e3e4b`.

_CI note added by Coder Agents on behalf of @nickvigilante._
</details>
2026-09-24 13:51:38 -04:00
Nick Vigilante eb893f536d docs: move section page to index.md (#29858) 2026-09-24 13:13:27 -04:00
Nick Vigilante 5e41baf73c docs: normalize control plane, coderd, and coder server terminology (#28960)
Normalizes the three overlapping names for the thing that serves the
Coder API and dashboard. This is step 1 of the Install/Deploy and
Administration refactor
([DOCS-685](https://linear.app/codercom/issue/DOCS-685),
[DOCS-738](https://linear.app/codercom/issue/DOCS-738)) and lands first
because every later page depends on settled terminology.

Resolves [DOCS-871](https://linear.app/codercom/issue/DOCS-871).

## The rule

| Term | Means | Use when |
|---|---|---|
| control plane | The component that serves the Coder API and dashboard,
brokers connections, and coordinates provisioners | Default in prose,
diagrams, planning, sizing, security |
| `coderd` | The process that implements it | Architecture internals,
metrics, logs, flags, troubleshooting |
| `coder server` | The literal CLI command | Only when the reader will
type it |

Retired: "the Coder server" as a name for the component, and "a service"
as a standalone description of it.

## What changed

- **`docs/.style/style-guide/word-choice.md`**: new "Naming Coder's
components" rule, with do/don't examples. Also fixes a "Do" example on
that page that itself used "the Coder server".
- **`docs/reference/glossary.md`**: rewrote `coderd` and Control plane,
added a `coder server` entry. The existing Control plane entry defined
it as "the collective term for `coderd`, its provisioners, and its
database", which made it indistinguishable from the Deployment entry two
sections later. Control plane is now the component; Deployment remains
the whole installation.
- **65 pages** across `docs/admin`, `docs/install`, `docs/get-started`,
`docs/tutorials`, `docs/user-guides`, `docs/ai-coder`, and `docs/about`:
roughly 154 component-sense "Coder server" usages, 21 bare "the server"
usages meaning the control plane, 3 "a service" descriptions, and 43
unbackticked `coderd` mentions in prose.
- One real bug caught in passing:
`docs/tutorials/best-practices/security-best-practices.md` documented
`coderd server --disable-path-apps`, which is not a command. Now `coder
server`.

Generated files were not touched: `configuration-reference.md`,
`prometheus.md`, `audit-logs.md`, `reference/cli/*`, `reference/api/*`,
and the generated blocks inside `feature-stages.md`. Those still contain
the retired term and need generator-side changes; see the deferred list
below.

## Reviewer attention, please

**Anchor changes.** 11 headings were renamed, so their fragments change.
No in-repo link targets any of them (the one internal link that did was
updated), but external deep links from tickets or blog posts would
break, and fragments cannot be fixed with a redirect. Happy to revert
any of these if you would rather they ride along with the page moves in
[DOCS-869](https://linear.app/codercom/issue/DOCS-869), which changes
those URLs anyway.

<details>
<summary>Renamed headings and their old fragments</summary>

| File | Old fragment | New heading |
|---|---|---|
| `admin/networking/index.md` | `#coder-server` | `## Control plane` |
| `install/index.md` | `#starting-the-coder-server` | `## Starting the
control plane` |
| `install/uninstall.md` | `#the-coder-server-binary-and-cli` | `## The
Coder binary and CLI` |
| `install/registry-mirror-artifactory.md` |
`#step-5-configure-coder-server-or-provisioners` | `## Step 5: Configure
the control plane or provisioners` |
| `install/cloud/compute-engine.md` | `#configuring-coder-server` | `##
Configuring the control plane` |
| `get-started/index.md` |
`#cant-start-coder-server-address-already-in-use` | ``### Can't start
`coder server`: Address already in use`` |
| `tutorials/external-database.md` |
`#coder-server-fails-startup-with-...` | ``### `coder server` fails
startup with ...`` |
| `tutorials/faqs.md` |
`#how-do-i-change-the-access-url-for-my-coder-server` | `## How do I
change the access URL for my control plane?` |
| `tutorials/best-practices/security-best-practices.md` |
`#coder-server` | `## Control plane` |
| `tutorials/best-practices/security-best-practices.md` |
`#server-logs-and-audit-logs` | `### Control plane logs and audit logs`
|
| `tutorials/best-practices/scale-coder.md` | `#coder-server`,
`#how-to-capture-coder-server-metrics-with-prometheus` | `## Control
plane`, `### How to capture control plane metrics with Prometheus` |

</details>

**One deliberate deviation from the issue.** DOCS-871 proposed reserving
"sub-agent" for the Coder Agents sense and using "devcontainer agent"
for the workspace sense. I did not make that change. The glossary
already disambiguates with "Sub-agent (Coder Agents)" and "Sub-agent
(dev containers)", and "devcontainer agent" would violate the existing
dev container spelling rule in the same style guide, which requires "dev
container" as two lowercase words. The current split reads correctly and
costs no churn. Happy to revisit if you disagree.

<details>
<summary>Deferred, with reasons</summary>

- **Generated content still says "Coder server".** `reference/cli/*`
comes from Go command help text, `configuration-reference.md` from
`configdocgen`, and `reference/api/*` from Swagger annotations. Fixing
those means changing Go source, which belongs in its own PR so a docs
review does not turn into a backend review.
- **`validated-architectures/index.md` "also known as `coderd`"** is
fixed here, but the page's larger problem is that it holds four
documents in one file. That split is
[DOCS-868](https://linear.app/codercom/issue/DOCS-868).
- **"server flag" phrasing** in `admin/users/idp-sync.md` and
`admin/users/oidc-auth/index.md` means `coder server` flags. Left alone;
rewording those sentences is a copy edit, not a substitution.
- **Metric and UI labels** such as "Dashboard-to-server latency" in
`admin/networking/index.md`. Renaming risks diverging from what the
dashboard shows.
- **Pre-existing `Coder.GerundHeading` warnings** on headings this PR
touches, and a "from from" typo in `scale-coder.md`. Out of scope for a
terminology pass.
- **Peer-sense "server"** in NAT traversal prose, where "client or
server" describes the connection roles rather than the control plane.
Needs restructuring, not substitution.

</details>

## Verification

| Check | Result |
|---|---|
| `make lint/emdash` | pass |
| `make lint/markdown` | pass, 504 files, 0 errors |
| `make lint/docs-html` | pass |
| `make lint/prose` | pass, findings identical to the `main` baseline (1
intentional demo error, 145 warnings, 1 suggestion; only Vale's file
ordering and one line-number shift in `glossary.md` differ) |
| `make gen` | clean, no drift |
| `make fmt/markdown` | applied |

No redirects needed: no page moved or was renamed in this PR.

<details>
<summary>Merged main, 2026-09-14</summary>

One conflict, in `docs/ai-coder/ai-gateway/providers.md`. Both hunks
were text this PR had edited and `main` had since deleted in #29053,
which removed env-based AI provider configuration:

- The deprecated provider seeding note, which held a `coderd` backtick
fix, is gone. Took `main`.
- The failure-mode table lost its env-drift row. Took `main`'s two rows
and reapplied the `coderd` logs wording.

`main` also landed new prose carrying the retired terms, so the pass was
extended to cover it:

- `docs/admin/integrations/oauth2-provider.md`: two "the server log(s)"
in the scope-enforcement sections added by #29144 are now "the `coderd`
log(s)"; "a `scope` the server will not grant" is now "the control
plane".
- `docs/reference/cli/index.md` gained "the server" from #29104, and
stays deferred as generated content.

The new `docs/ai-coder/agent-relay/claude-code.md` needed no change, and
no page `main` added links to a heading this PR renamed.

</details>

<details>
<summary>Decision log for the wider refactor</summary>

Agreed with @nickvigilante before implementation, recorded on DOCS-685
and in the Notion outline:

1. Feature stages and release channels move to Reference; ESR upgrade
guides move under Operate. No new top-level Releases section.
2. `data-retention.md` moves to Administration > Security;
`chat-lifecycle-hooks.md` moves into the Coder Agents docs.
`admin/setup/` therefore survives as "Deployment configuration" and
`configdocgen` is not touched.
3. Prepare is advisory, not a gate, framed as "don't know where to
start? Start here", with lead times and owners per item.
4. One actor table, on the Install overview, spanning all five phases,
noting that one person can be several actors and several people can be
one actor.
5. The quickstart stays independent and is mentioned once, on the
Install overview. The proposed `server/single-machine` page is dropped.
6. Coder Validated Architecture keeps its name in nav, defined in-page,
with a glossary entry.
7. PR 3 is split: moves only, then prose, so the riskiest change reads
as a pure rename diff.
8. For every PR that moves a page, the coder.com `redirects.json` change
merges first.
9. There is no Download section and no CLI page in docs. Per-OS CLI
steps live on coder.com/download and the Install overview links to it.

</details>

Generated with Coder Agents on behalf of @nickvigilante.
2026-09-22 15:18:40 -04:00
Ben Potter dde8069ade docs(docs/tutorials/reverse-proxy-caddy.md): clarify the Caddy on-demand TLS ask endpoint (#29269)
The Caddy reverse-proxy tutorial's `on_demand_tls` example told readers
to leave `ask http://example.com` unchanged, warning that changing it
could break cert issuance. That's backwards: Caddy calls the `ask` URL
with the requested hostname as a `domain` query parameter and treats any
`2xx` response as approval to issue a certificate. `example.com` returns
`200` for any request, so the documented config approved certificate
issuance for any hostname, letting an attacker exhaust the deployment's
Let's Encrypt rate limit for the whole domain.

Replaces the "DO NOT CHANGE" warning with guidance to point `ask` at a
real, self-hosted validation endpoint, and links to [the
discussion](https://github.com/coder/coder/discussions/29079#discussioncomment-18462842)
that reported this for a reference implementation.

Reported in https://github.com/coder/coder/discussions/29079

> This PR was created with AI assistance (Coder Agents).
2026-09-17 18:32:18 -05:00
Nick Vigilante 7846490770 docs: reconcile H1/label mismatches into front-matter titles (Phase 3) (#28036)
## Summary

**Batch B of Phase 3** of the H1 → front-matter migration (`DOCS-484`;
parent `DOCS-477`), the editorial follow-up to Batch A (#28030). For the
**124** hand-authored pages whose leading body H1 differed from the
manifest nav label, this promotes the richer body H1 to the front-matter
**page title** and drops the duplicate H1, while keeping the short
manifest **label** as the sidebar entry.

Policy agreed with the docs owner:
- Short sidebar labels win. Duplicate sidebar labels are acceptable.
- Identical **page titles** across two pages are not. On a collision,
rename the non-generated page's title.

## Page-title collisions resolved

Promoting the H1 created 4 page-title collisions; each is disambiguated
by renaming the non-generated / secondary page (sidebar labels left
unchanged):

| page | sidebar label | body H1 | → page title |
|------|---------------|---------|--------------|
| `install/index.md` | Install | Installing Coder | **Install Coder in
your infrastructure** |
| `install/cli.md` | Coder CLI | Installing Coder | **Install the Coder
CLI** |
| `ai-gateway/clients/jetbrains.md` | JetBrains | JetBrains IDEs |
**JetBrains** |
| `workspace-access/web-ides.md` | Web IDEs and Coder Apps | Web IDEs |
Web IDEs *(kept — primary user guide)* |
| `extending-templates/web-ides.md` | Web IDEs and Coder Apps | Web IDEs
| **Web IDEs in templates** |
| `workspace-management.md` | Workspace Management | Workspaces |
**Manage workspaces** *(vs generated `api/workspaces.md`)* |

These titles are the only non-mechanical editorial calls in this PR.

## Deliberately excluded: generated pages

`admin/security/audit-logs.md` and
`admin/setup/configuration-reference.md` are `make gen` outputs
(`auditdocgen` / `configdocgen`) that Phase 2 did **not** update, so a
hand-added title would be reverted on the next generation. They're
excluded here and need their generators taught to emit front matter
first (Phase 2 pattern), tracked as `DOCS-631`. Batch A (#28030) had the
same issue for three other generated pages; it has been corrected there
too.

## Verification

AI was the primary author of this PR (see disclosure below); per the [AI
Contribution
Guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
here is the manual verification.

- Promote = set front-matter `title` to the body H1 (or the
disambiguated title above) and remove that one duplicate H1. No other
body prose is reflowed.
- Every added front-matter block parses as YAML; all files pass `pnpm
check-docs` (markdownlint-cli2 + table formatter): `Summary: 0
error(s)`.
- Removing the H1 orphaned one in-page fragment (`[MCP](#mcp)` in
`ai-gateway/mcp.md`), now repointed. A cross-file fragment audit into
the retitled pages found no real breakage.


Anchor-change note

Unlike Batch A (title unchanged, a rendered no-op), Batch B changes each
page's effective title from the manifest label to the body H1, so the
rendered H1 **anchor slug changes**. An audit of cross-file
`.md#fragment` links into these pages surfaced no links that break as a
result (the only hits were false positives against a still-present `##
TLS & Reverse Proxy` heading). Same-file fragments are enforced by
markdownlint MD051, which is green.

## Title casing (follow-up, DOCS-742)

A follow-up commit puts the promoted titles into sentence case and
removes leading gerunds (for example, `Managing workspace schedules` →
`Manage workspace schedules`): 57 of the 120 titles changed, the other
63 were already compliant. Proper nouns and branded names (Coder
Validated Architecture, JetBrains, File Browser, Dev Containers,
Island), acronyms (API, CLI, IDE, MCP, OIDC, URL, UI, CI/CD), the
Premium license tier, and maturity labels (Beta, Experimental) are
preserved. The four `Nk-users` pages become `Reference architecture: up
to N users` (lowercase, matching the CVA landing page's own usage); the
CVA landing page keeps its branded title. `pnpm check-docs`: `Summary: 0
error(s)`.

Linear: DOCS-484, DOCS-742

> This PR was created with AI assistance (Coder Agents).
2026-09-08 16:34:00 +00:00
Nick Vigilante bc77c40296 docs: add front-matter titles to mechanical and no-H1 pages (Phase 3) (#28030)
## Summary

**Batch A of Phase 3** of the H1 → front-matter migration (`DOCS-484`;
parent `DOCS-477`). Adds a front-matter `title` to every navigable docs
page whose title can be migrated **mechanically**, with no editorial
judgment.

This is the content step that Phase 1 (renderers prefer front-matter
title, `DOCS-482`) and Phase 2 (tooling + generators front-matter-aware,
`DOCS-483`) unblocked. Both are merged; coder.com #964/#974 are merged
and live.

**Rendered no-op.** The renderers already resolve the page title from
the manifest and hide the leading body H1 (Phase 1), so no page changes
visually. This just moves the title into front matter where Fumadocs and
the migrated tooling can read it.

## What's in this batch

Dry-run on `main` (464 navigable pages) splits into:

| category | count | this PR |
|----------|-------|---------|
| already has front-matter title (Reference, from Phase 2 generators) |
196 | skipped (idempotent) |
| **mechanical** — leading body H1 equals the manifest label | 138 | ✅
add front-matter `title`, drop the duplicate H1 |
| **no body H1** — renders under the manifest label only | 4 | ✅ add
front-matter `title` only |
| **mismatch** — body H1 differs from the manifest label | 126 | ⏭️
deferred (needs an editorial decision, see below) |

142 files changed, all under `docs/`.

## Deliberately out of scope: the 126 mismatches

Pages where the body H1 is richer than the short sidebar label (e.g.
label **Modules** / H1 *Contributing modules*, label **Install** / H1
*Installing Coder*) need a canonical-title decision, not a script. A few
even look like the body H1 is the redundant one (`install/cli.md` and
`install/index.md` both carry the H1 *Installing Coder*). These will
land in follow-up batches **by nav section** once the policy is set, so
each gets real review.

## Verification

AI was the primary author of this PR (see disclosure below); per the [AI
Contribution
Guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
here is the manual verification.

- Every added front-matter block parses as YAML and its `title`
round-trips to the manifest label (checked programmatically across all
142 files).
- Every removed line is a leading `# H1` that equalled the manifest
title; no body prose was reflowed. Front matter is correctly hoisted
above pre-existing `<!-- markdownlint-disable -->` comments on the two
pages that had them.
- `pnpm check-docs` (markdownlint-cli2 + table formatter) passes on the
changed set: `Summary: 0 error(s)`. `MD041` stays off (re-enabled in
Phase 4); `MD025` is not tripped because the duplicate body H1s are
removed.

```
$ pnpm exec markdownlint-cli2 $(git diff --name-only origin/main)
Linting: 142 file(s)
Summary: 0 error(s)
```

Linear: DOCS-484

> This PR was created with AI assistance (Coder Agents).
2026-08-27 12:34:13 -04:00
Nick VigilanteandIan Evans 47a77016c4 feat(docs/.style): enable Coder.SelectClick, use "select" over "click" (#28599)
Adds a Vale rule enforcing the existing "select, not click" style
guidance: "click" assumes the reader has a mouse, and touch, keyboard,
and assistive-technology users don't click.

`Coder.SelectClick` (warning severity) flags
`click`/`clicks`/`clicked`/`clicking` in prose, exempting:

- Literal mouse-button phrasing (`left-click`, `right-click`,
`middle-click`, `mouse click`, and combinations) — these name an actual
mouse action with no device-agnostic equivalent.
- `one-click`/`single-click` as a compound feature descriptor
("one-click install").

Reworded every existing instructional "click" in `docs/` to the matching
`select` form (`select`/`selects`/`selections`/`selected`/`selecting`)
so the rule ships with a clean baseline, per this repo's Vale-rule
doctrine.

Left alone, deliberately: two generated API reference pages
(`docs/reference/api/enterprise.md`, `schemas.md`) use "click" as an
analytics-event noun, tied to a Go source doc comment and an operation
ID (`report-a-premium-paywall-click`). Fixing those means editing
generated code and an API operation ID, which is out of scope for a
docs-style PR — see the rule's own comments for the reasoning. That
leaves 2 non-zero-baseline findings for this rule.

---
🤖 Built with AI assistance.

---------

Co-authored-by: Ian Evans <5760366+ianjevans@users.noreply.github.com>
2026-08-27 16:24:06 +00:00
Nick Vigilante b0e93b6e3b docs: correct nginx X-Forwarded-Proto and certbot instructions flavor (#28086)
## What

Two fixes to the nginx reverse-proxy tutorial.

### `X-Forwarded-Proto` (line 137)
The config set:
```nginx
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
```
`$http_x_forwarded_proto` is the value of a client-supplied request
header, which a client can spoof and which is usually empty for a direct
request. In an nginx TLS-terminating reverse proxy this should be
`$scheme`, which nginx sets from the actual connection (`https`). Using
the raw client header can break Coder's scheme detection and
secure-cookie handling.

### Certbot link flavor (line 57)
The Certbot instructions link used `?ws=apache` in an nginx guide;
changed to `?ws=nginx` so readers get nginx instructions.

Surfaced by the runtime drift sweep; verified against `main`.

Linear:
[DOCS-642](https://linear.app/codercom/issue/DOCS-642/docs-fix-reverse-proxy-nginx-x-forwarded-proto-dollarscheme-certbot)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 12:47:59 -04:00
Nick Vigilante f3fd4c4a77 docs: remove invalid --yes flag from coder template version promote (#28084)
## What

Remove the invalid `--yes` flag from the `coder template version
promote` command in the CI/CD publishing example.

## Why

`docs/tutorials/testing-templates.md` documents, in the GitHub Actions
"Promote template version" step:

```
coder template version promote --template=$TEMPLATE_NAME --template-version=... --yes
```

The `promote` subcommand has no `--yes`/confirmation flag, so the
command exits with `unknown flag: --yes` and breaks the documented CI
workflow. This is a golden-path (automation) breaker.

Verified against the generated reference
`docs/reference/cli/templates_versions_promote.md` (flags are only
`--template`, `--template-version`, `-O/--org`), and reproduced against
a live deployment during the runtime drift sweep. The command is
non-interactive, so no confirmation flag is needed.

## Change

Single line: drop ` --yes`.

Linear:
[DOCS-640](https://linear.app/codercom/issue/DOCS-640/docs-remove-invalid-yes-flag-from-coder-template-version-promote)

> This PR was created with AI assistance (Coder Agents).
2026-08-14 09:41:43 -07:00
Nick Vigilante c84aa564ba docs: normalize code-fence languages for Shiki compatibility (#27161)
Normalizes non-standard code-fence language tags across `docs/**` so a
strict highlighter (Shiki, used by Fumadocs) won't fail the build on an
unrecognized language, and unifies redundant synonym tags onto one
canonical form per language. The current renderer (Speed-Highlight)
detects the language from the code content, not the fence label, so this
drift wasn't visible until now.

## Changes

- `hcl` -> `tf` (199 fences, including indented ones nested in
numbered/bulleted lists). Shiki ships `hcl` and `terraform` as two
distinct grammars (not aliases); every `hcl`-tagged fence in `docs/**`
is actually Terraform resource/data/provider syntax, so the more
specific `terraform` grammar is correct for all of them. `tf` is Shiki's
own alias for that grammar, and it's also what GitHub's own markdown
renderer resolves to the same HCL/Terraform highlighting.
- `pwsh`/`powershell` -> `ps1`. Both `ps` and `ps1` are registered
PowerShell aliases in Shiki, but on GitHub's renderer only `.ps1` is a
registered file extension (`.ps` isn't), so `ps1` renders identically to
`powershell` there today while bare `ps` would silently lose
highlighting.
- `env` -> `dotenv` (a dedicated Shiki grammar for `KEY=VALUE` files)
- `text`/`output`/`none`/`url` -> `txt`. Same built-in plain-text
fallback either way, just shorter.
- `Dockerfile` -> `dockerfile` (lowercase)
- `bash`/`shell` -> `sh` (732 fences). Shiki and GitHub both alias all
three to a single shell grammar; this was already the style guide's
stated preference, just not enforced across the existing corpus until
now.
- `markdown` -> `md` (4 fences). Alias of the same grammar in both Shiki
and GitHub.
- `jsonc` -> `json` (1 fence). The block has no comments or trailing
commas, so it doesn't need the comments-capable grammar.
- `ts` -> `tsx` (2 fences, `docs/about/contributing/frontend.md`).
Verified the actual content tokenizes identically under both grammars,
and a sibling block in the same file already needs `tsx` for real JSX,
so unifying to one tag is safe for this file. Documented a caveat: `tsx`
mis-tokenizes the legacy angle-bracket type-assertion syntax
(`<Type>value`), which is invalid in real `.tsx` files anyway, so use
`value as Type` instead.
- `yml` -> `yaml` (1 fence)
- Updated `docs/.style/style-guide/formatting.md` to document all
canonical tags

`promql` (2 fences) and `caddyfile` (2 fences) are left as-is. Shiki
doesn't bundle a grammar for either, so they need a custom grammar
registration when the site adopts Shiki, rather than degrading to `txt`.
Tracked as follow-up work under DOCS-118 and
[DOCS-544](https://linear.app/codercom/issue/DOCS-544/vendor-a-local-promql-grammar-for-shiki-syntax-highlighting)
(promql).

Does not touch `offlinedocs/`.

Linear:
[DOCS-476](https://linear.app/codercom/issue/DOCS-476/normalize-docs-code-fence-languages-de-risk-shikifumadocs)

<details>
<summary>How the fence tags were verified</summary>

Each tag was tested against a real `shiki@latest` highlighter instance
(`codeToHtml`/`codeToTokens`) and cross-checked against GitHub's
`@wooorm/starry-night` grammar sources (the renderer that actually
displays these `.md` files today, in repo browsing and PR diffs), since
that's what determines whether brevity is safe before Shiki adoption:

```text
FAIL  env        -- Language `env` is not included in this bundle.
FAIL  Dockerfile -- Language `Dockerfile` is not included in this bundle.
FAIL  promql     -- Language `promql` is not included in this bundle.
FAIL  caddyfile  -- Language `caddyfile` is not included in this bundle.
FAIL  pwsh       -- Language `pwsh` is not included in this bundle.
FAIL  output     -- Language `output` is not included in this bundle.
```

`hcl` doesn't error in Shiki, since it's a real grammar, but that's
exactly the trap: it was silently rendering every fence with the generic
HCL grammar instead of the Terraform-specific one. Every `hcl`-tagged
fence in `docs/**` was manually checked against `origin/main` and is
genuinely Terraform content.

For `ts`/`tsx`, tokenizing the actual doc content confirmed identical
output under both grammars; a synthetic test with the legacy
angle-bracket cast syntax confirmed `tsx` degrades on that specific
construct, which the style guide now calls out.

The first normalization pass only matched fence tags at column 0
(`^```tag$`), missing tags indented inside numbered/bulleted lists. A
follow-up pass caught the remaining occurrences at any indentation
level.

</details>


---

*This PR description and the underlying changes were prepared with Coder
Agents assistance.*
2026-07-15 14:07:09 -04:00
Nick Vigilante 199b5936c1 fix(docs): replace invalid </br> tags and format swallowed placeholder URL (#27174)
## Summary

Fixes two classes of invalid/broken HTML in hand-written docs. Both are
visible problems in today's rendered docs, independent of any
docs-engine work.

1. **`</br>` is not a real HTML tag.** `br` is a void element with no
closing form; browsers error-correct `</br>`, but it is invalid HTML.
Replaced all 15 usages with `<br />` across:
   - `docs/admin/templates/extending-templates/dynamic-parameters.md`
   - `docs/admin/users/idp-sync.md`
   - `docs/tutorials/best-practices/organizations.md`
2. **Browser-swallowed placeholder URL.** In
`docs/ai-coder/github-to-tasks.md`,
`https://<your-coder-url>/settings/external-auth` was unformatted, so
HTML renderers parse `<your-coder-url>` as an unknown tag and drop it.
The live docs currently render the broken text `re-authenticate at
https:///settings/external-auth`. Wrapped in backticks, matching every
other instance in the same file.

Table realignment noise in the diff is from `fmt/markdown` (`<br />` is
one character wider than `</br>`).

A repo-wide grep confirms no remaining `</br>` and no other unformatted
`https://<placeholder>` URLs in prose (other hits are inside code fences
or already backticked). The equivalent placeholder issues in
**generated** reference docs (CLI help strings, swagger annotations) are
intentionally out of scope and tracked separately in
[DOCS-551](https://linear.app/codercom/issue/DOCS-551/backtick-placeholder-syntax-in-generated-reference-docs-cli-help).

Tracking issue:
[DOCS-550](https://linear.app/codercom/issue/DOCS-550/fix-invalid-br-tags-and-browser-swallowed-placeholder-url-in-hand)

---

Created by Coder Agents on behalf of @nickvigilante.
2026-07-13 10:41:38 -04:00
Nick Vigilante f7632451f4 feat(docs): add Coder.BrandNames Vale rule, enforce HashiCorp casing (#25501)
Lands the first concrete rule under the `Coder` style:
`Coder.BrandNames`, a bundled `substitution` rule that enforces
canonical brand casing in prose. HashiCorp is the first entry;
[DOCS-188](https://linear.app/codercom/issue/DOCS-188) extends it with
GitHub, OpenTofu, Kubernetes, Terraform, JetBrains, and VS Code.

## What changes

Four commits, ordered so each is independently valid:

1. **`docs: fix HashiCorp casing in prose and sidebar`**
([06d769dad1](https://github.com/coder/coder/pull/25501/commits/06d769dad179cf85c535b058df3b6bafdc1f9565)).
5 Markdown files plus 2 `docs/manifest.json` entries. Drives the corpus
violation count to zero.
2. **`feat(docs/.style/styles/Coder): add Coder.BrandNames Vale rule`**
([e00fc780a7](https://github.com/coder/coder/pull/25501/commits/e00fc780a7a20dcf82105d997af5cfcddd4b1855)).
New `BrandNames.yml` with the HashiCorp swap at `level: error`, plus a
new `### Brand names` subsection in `docs/.style/style-guide.md`.
3. **`docs(.style/styles/Coder/README.md): scrub planned-rules notes
obsoleted by Coder.BrandNames`**
([af8833b9f5](https://github.com/coder/coder/pull/25501/commits/af8833b9f58dd617732ae533bbf53eb4fc2e816a)).
Removes the README's "intentionally empty for now" lead-in and the
obsolete HashiCorp casing bullet from the planned-coverage list.
4. **`docs: apply semantic line breaks and fix Vale findings on
PR-touched files`**
([e9f11df188](https://github.com/coder/coder/pull/25501/commits/e9f11df1886fdf0d5efc5e8a2cab95fecbd898f9)).
Pre-review pass on every Markdown file this PR modifies. Full sembr and
Vale-warning cleanup on the style-guide infrastructure
(`style-guide.md`, `Coder/README.md`); sembr applied to the HashiCorp
swap paragraph only on the five product docs, per scoping discussion
with @nickvigilante.

## Severity rationale

`error` from day one. HashiCorp's brand owner publishes a canonical
casing; any other casing in prose is wrong, not a judgment call. Matches
the `error = low FPs x high gravity` framework. False-positive rate is
effectively zero because Vale's `substitution` rule skips inline code,
fenced code blocks, and URLs by default, so `hashicorp/kubernetes`
(Terraform provider source) and `developer.hashicorp.com` stay
untouched.

## Verification

- `make lint/markdown`: 0 errors across 487 files.
- `make lint/prose`: 1 error, 1 warning, 1 suggestion in 468 files. All
three findings are the intentional `Coder.DemoError`,
`Coder.DemoWarning`, and `Coder.DemoSuggestion` annotations on
`docs/.style/style-guide/demo/demo.md` (added on main as part of the
[DOCS-425](https://linear.app/codercom/issue/DOCS-425) inline-annotation
demo), not real findings. `Coder.BrandNames` fires zero times against
the cleaned-up corpus.
- `make pre-commit-light`: passed (7s).
- Self-test: ran the rule against an unmodified `docs/` and confirmed it
flags the 7 prose instances the cleanup commit fixes, then re-ran
against the post-cleanup state and confirmed zero alerts.

## Known future conflict

When [#26632](https://github.com/coder/coder/pull/26632)
([DOCS-434](https://linear.app/codercom/issue/DOCS-434)) merges, the
monolithic `docs/.style/style-guide.md` is split into the
`docs/.style/style-guide/` multi-page structure. The `### Brand names`
subsection added in commit 2 will need to land in
`docs/.style/style-guide/word-choice.md` (which already references the
rule), and the `link:` in `docs/.style/styles/Coder/BrandNames.yml` will
need to update from `style-guide.md#brand-names` to
`style-guide/word-choice.md#brand-names`. Resolution path documented in
an inline comment on this PR.

<details>
<summary>Implementation plan and decision log</summary>

### Why bundle into Coder.BrandNames rather than one file per brand

Vale's convention (mirrored by `Google.WordList` with ~70 swaps in a
single file) is to bundle `substitution` rules when they share severity,
message template, and link. All brand-name rules share that shape:
`error`, `Use '%s' instead of '%s'`, link to the style guide section.
Bundling reduces "add a brand" to a one-line YAML diff and keeps
`CODEOWNERS` and blame coherent. Per-rule performance is irrelevant at
this scale; Vale's per-rule overhead is sub-millisecond and dwarfed by
Markdown parsing.

### Why the cleanup lands first

Commits are ordered cleanup-then-rule so each commit is a known-good
state:

- After commit 1: corpus is HashiCorp-clean, but no rule exists yet.
- After commit 2: rule exists and lints a clean corpus.

Reversing the order would land the rule at commit 1 (firing 7 errors on
uncleaned content) and resolve them at commit 2. Under `--no-exit` the
CI job still passes, but the inline annotations on commit 1 would be
misleading.

### Why HashiCorp first instead of all brands at once

Proof-of-concept value. HashiCorp is the smallest cleanup (7 prose lines
plus 2 sidebar lines = 9 lines), zero FPs, zero ambiguity. Once the loop
(rule plus cleanup plus style-guide section) is proven,
[DOCS-188](https://linear.app/codercom/issue/DOCS-188) appends the other
brands as additional commits to the same bundle.

### Brand-token sensitivity

The `swap:` table only matches:

- `Hashicorp` (capital H, lowercase rest), the actual wrong form in the
corpus.
- `HASHICORP` (all caps), defensive; doesn't appear in current corpus
but cheap to include.

`hashicorp` (all lowercase) is **not** in the swap table. The lowercase
form appears 49 times in URLs (`developer.hashicorp.com`,
`registry.terraform.io/providers/hashicorp/...`,
`github.com/hashicorp/...`) and 6 times as Terraform provider sources
(`source = "hashicorp/kubernetes"`), all of which are correct lowercase
by convention. Vale's substitution rule scope ensures URLs and code
blocks are skipped, but skipping the rule entirely for `hashicorp`
(lowercase) is the explicit decision; if a prose typo of lowercase
"hashicorp" ever shows up, we'd catch it through `Vale.Spelling`
([DOCS-187](https://linear.app/codercom/issue/DOCS-187)) instead.

### Self-reference in the style guide

The `### Brand names` section's example table needed `Hashicorp` and
`HashiCorp` as literal demonstration tokens. Wrapping them in backticks
(`` `Hashicorp` ``, `` `HashiCorp` ``) keeps Vale from flagging the
wrong-case example as a real violation. This is correct typography too:
demonstration tokens get code formatting.

### Manifest.json

Vale doesn't lint JSON, so the two `docs/manifest.json` entries are
fixed by direct edit rather than tool enforcement. The sidebar `path`
(`./admin/integrations/vault.md`) is unchanged; the title change does
not affect the page URL on coder.com. No redirect needed in
`coder/coder.com:redirects.json`.

### Pre-mortem

- **Generated docs noise**: `Coder.BrandNames` does not fire on
auto-generated `docs/reference/` content because no codersdk identifier
matches the swap pattern. Zero risk.
- **Future-additions friction**: adding GitHub to the swap table is one
YAML line and a cleanup commit. The bundling shape pays off here.
- **Disable footgun**: if a contributor needs to write the wrong casing
on purpose (quoting an external bug report verbatim, for example), they
can wrap the literal in backticks (already correct typography) or use
the per-line Vale skip comment.

</details>

Closes [DOCS-34](https://linear.app/codercom/issue/DOCS-34).

---

*Filed via [Coder Agents](https://coder.com/docs/ai-coder/agents) on
Nick's behalf.*
2026-07-08 11:43:29 -04:00
Jeremy Ruppel 79fc8541ed docs: update template creation docs for template builder (#26993)
## Summary

Update documentation across 9 files to present the template builder as
the primary template creation method, replacing the old starter
templates flow as the default entry point.

The template builder is a guided wizard that lets admins select base
infrastructure, add registry modules, configure variables, and produce
validated Terraform without writing HCL.

## Changes

**Primary docs (significant rewrites):**
- `docs/admin/templates/creating-templates.md`: Added "Using the
template builder" as the first section with full 5-step wizard
documentation, screenshots, airgap/registry notes, and alternative
creation links. Moved CLI starter template flow to its own section.
Fixed "You can the" typo.
- `docs/get-started/index.md`: Rewrote Steps 4-6 to use the builder with
the Docker base template instead of the Coder Quickstart (which is not a
builder base template). Generalized workspace parameter instructions.
- `docs/start/first-template.md`: Rewrote to use the builder. Removed
old starter templates references, TODO notes, typo, and commented-out
sections.

**Secondary docs (targeted edits):**
- `docs/admin/templates/index.md`: Replaced starter templates section
with builder-first "Create a template" section.
- `docs/admin/templates/managing-templates/index.md`: Renamed "Starter
templates" to "Creating templates" pointing to the builder.
- `docs/install/airgap.md`: Added "Template builder" section documenting
`CODER_DISABLE_TEMPLATE_BUILDER` and
`CODER_TEMPLATE_BUILDER_REGISTRY_URL`.
- `docs/tutorials/template-from-scratch.md`: Added TIP callout
recommending the builder. Fixed `coder templates create` -> `coder
templates push` inconsistency.
- `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`:
Updated Dashboard tab to reference the builder and "Upload an existing
template" alternative.
- `docs/about/screenshots.md`: Updated caption and image reference for
template builder.

**Screenshots added:**
- `templatebuilder_01_bases.png` (base selection step)
- `templatebuilder_02_modules.png` (module selection step)
- `templatebuilder_03_module_customization.png` (module settings step)
- `templatebuilder_04_customizations.png` (template customizations step)

<details>
<summary>Implementation plan</summary>

# Plan: Update docs/ for Template Builder Launch

## Summary

The Template Builder is a new guided wizard at `/templates/new/builder`
that lets admins create templates by selecting a base infrastructure
template, composing it with registry modules, configuring variables, and
producing a validated Terraform bundle without writing HCL. The docs
need to be updated to present this as the primary/recommended template
creation path, while preserving the existing paths (upload, CLI,
duplicate) as alternatives.

## Key behavioral facts from the code

- **Route**: `/templates/new/builder` (new), `/templates/new` (old,
still exists)
- **Entry point**: The "New Template" button on the Templates page links
to `/templates/new/builder` when the builder is enabled; otherwise falls
back to `/starter-templates`
- **5-step wizard**:
  1. **Select base infrastructure** (e.g., Docker, AWS EC2, Kubernetes)
  2. **Base template parameters** (optional, skipped if base has none)
3. **Select modules** (IDE, AI Agent, Source Control, etc.;
multi-select, grouped by category)
4. **Module settings** (optional, skipped if no configurable variables)
5. **Template customizations** (name, display name, description, icon,
organization)
- **Alternative creation links** are shown on step 1: "Start from
scratch", "Upload an existing template", "Browse community templates",
"Use template agent skill"
- **Disabled via**: `CODER_DISABLE_TEMPLATE_BUILDER` env var /
`--disable-template-builder` flag. When disabled, redirects to old
`/templates/new` flow
- **Registry URL override**: `CODER_TEMPLATE_BUILDER_REGISTRY_URL`
(default: `registry.coder.com`)
- **Requires outbound access** to `registry.coder.com` for `terraform
init` at compose time
- **Modules are bundled** with the Coder release binary; the builder
does not fetch metadata from the registry at runtime
- **Sensitive variables** (secrets) are not collected by the builder;
they are deferred to workspace creation time
- **Module conflicts** show a warning but do not block creation
- **One-way**: No re-entry into the builder for existing templates; edit
HCL directly after creation

## Files to update

### Tier 1: Primary creation flow docs (significant rewrites)

#### 1. `docs/admin/templates/creating-templates.md`

**Current state**: Documents three creation paths: "From a starter
template" (primary), "From an existing template", "From scratch
(advanced)".

**Changes**:
- Add a new section **"Using the template builder"** as the first and
primary section (before "From a starter template").
- Describe the 5-step wizard flow: select base infrastructure, configure
base parameters, select modules, configure module settings, set template
customizations.
- Mention that the builder is enabled by default and requires outbound
access to `registry.coder.com`.
- Note that sensitive variables are collected from developers at
workspace creation, not during template building.
- Add a callout about disabling the builder for airgapped deployments
(`CODER_DISABLE_TEMPLATE_BUILDER`).
- Note the `CODER_TEMPLATE_BUILDER_REGISTRY_URL` option for self-hosted
registry mirrors.
- Keep existing "From a starter template", "From an existing template",
and "From scratch" sections largely intact, but reframe them as
alternative paths.
- Update the "From a starter template" Web UI instructions to note the
new entry point routing (the "New Template" button now goes to the
builder when enabled).
- Fix existing typo: "You can the [Coder CLI]" should be "You can use
the [Coder CLI]".

#### 2. `docs/start/first-template.md`

**Current state**: Beginner tutorial walking through creating a template
from the Docker starter template via the old flow. Has a typo (`s` at
end of line 32), commented-out workspace creation section, and TODO
notes.

**Changes**:
- Rewrite steps 2 and 3 to use the Template Builder as the primary path.
- Step 2: Navigate to **Templates**, select **New Template**, which
opens the Template Builder.
- Step 3: Walk through the builder wizard steps (select Docker base,
optionally select modules like code-server, configure template
name/description, create).
- Remove the typo on line 32 (`s`).
- Keep the "Modify your template" section (step 6) intact since it
covers post-creation editing which is unchanged.
- Remove or update the reference to "Starter Templates" as a separate
page since the builder subsumes that entry point.

#### 3. `docs/get-started/index.md`

**Current state**: Quickstart guide. Step 4 says "Select **Templates** →
**New Template**" then pick "Coder Quickstart" from starter templates.

**Changes**:
- Update Step 4 to describe using the Template Builder.
- The flow becomes: Select **Templates** → **New Template** → builder
opens → select **Coder Quickstart** as the base template → optionally
add modules → set name/description → **Create Template**.
- Update the "What just happened?" explanation to mention the builder
composed and validated the Terraform.
- Screenshot reference `create-quickstart-template.png` will need a new
screenshot (note this in the PR; screenshots are out of scope for this
change but should be flagged).

### Tier 2: Secondary references (targeted edits)

#### 4. `docs/admin/templates/index.md`

**Current state**: Overview page mentioning starter templates as the
primary creation path.

**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the recommended way to create templates, with starter templates
serving as base templates within the builder.
- Update the link to point to the builder section: `[Create a template
with the template
builder](./creating-templates.md#using-the-template-builder)`.
- Update the screenshot reference and caption. The "Starter Templates"
page screenshot may no longer be the first thing admins see.

#### 5. `docs/admin/templates/managing-templates/index.md`

**Current state**: Documents starter templates, editing, updating,
deleting.

**Changes**:
- Update the "Starter templates" section to mention the Template Builder
as the primary creation path, with starter templates available as base
templates within it.
- Update the image reference from `starter-templates.png` if it shows
the old flow.

#### 6. `docs/tutorials/template-from-scratch.md`

**Current state**: Detailed tutorial for writing a template from scratch
with Terraform.

**Changes**:
- Add a brief note at the top recommending the Template Builder for
users who want to create templates without writing Terraform, with a
link to
`docs/admin/templates/creating-templates.md#using-the-template-builder`.
- In section "7. Create the template in Coder" → "Dashboard" tab, update
the UI steps. The "Upload template" option is now accessed via the old
creation flow at `/templates/new` (or through the "Upload an existing
template" link in the builder's alternatives).
- Fix the inconsistency where text says `coder templates create` but the
code block uses `coder templates push`.

#### 7.
`docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`

**Current state**: Documents creating envbuilder templates via
Dashboard, CLI, and Registry tabs.

**Changes**:
- In the Dashboard tab, update the instructions. The "Create Template"
button now opens the builder by default. Users need to use the "Upload
an existing template" alternative link or navigate to `/templates/new`
directly.
- Update "From scratch" reference since that option is now an
alternative link in the builder.
- The CLI and Registry tabs remain unchanged.

#### 8. `docs/install/airgap.md`

**Current state**: Documents air-gapped installations. No mention of
Template Builder.

**Changes**:
- Add a note in the relevant section about the Template Builder
requiring outbound access to `registry.coder.com`.
- Document `CODER_DISABLE_TEMPLATE_BUILDER` for fully air-gapped
deployments.
- Document `CODER_TEMPLATE_BUILDER_REGISTRY_URL` for deployments using a
self-hosted registry mirror.

#### 9. `docs/about/screenshots.md`

**Current state**: Contains a caption "Template administrators can
either create a new Template from scratch or choose a Starter Template".

**Changes**:
- Update the caption to mention the Template Builder as the primary
creation method.
- Screenshot reference may need updating (flag for new screenshot).

### Tier 3: Minor/link-only updates

#### 10. `docs/admin/users/organizations.md`

- If it references the old "Create Template" screen with an org picker,
add a note that the Template Builder also includes organization
selection in its final step.

#### 11. `docs/ai-coder/tasks.md`

- If it mentions creating templates, add a passing reference to the
Template Builder as an option.

## Files NOT to update

- `docs/reference/api/templatebuilder.md`: Auto-generated API reference.
Already correct.
- `docs/reference/api/schemas.md`: Auto-generated. Already correct.
- `docs/reference/cli/server.md`: Auto-generated. Already has
`--disable-template-builder` and `--template-builder-registry-url`.
- `docs/reference/cli/templates_create.md`: Already deprecated.
- `docs/reference/cli/templates.md`: No changes needed.

## Implementation order

1. `docs/admin/templates/creating-templates.md` (primary creation docs,
most content)
2. `docs/get-started/index.md` (quickstart)
3. `docs/start/first-template.md` (beginner tutorial)
4. `docs/admin/templates/index.md` (overview)
5. `docs/admin/templates/managing-templates/index.md` (managing
overview)
6. `docs/install/airgap.md` (airgap note)
7. `docs/tutorials/template-from-scratch.md` (from-scratch tutorial)
8. `docs/admin/integrations/devcontainers/envbuilder/add-envbuilder.md`
(envbuilder)
9. `docs/about/screenshots.md` (screenshot captions)
10. Minor link/reference updates in tier 3 files

## Style notes

- Follow the Diataxis framework; keep tutorials as tutorials, reference
as reference.
- Use present tense, active voice, second person.
- Bold for UI elements: **Templates**, **New Template**, **Create
Template**.
- No emdash/endash.
- Do not add screenshots; flag where new screenshots are needed as
comments/TODOs.
- Run `make fmt/markdown` and `make lint/markdown` after all changes.
- Verify all pages are already in `docs/manifest.json` (no new pages
being added, only existing pages being updated).

</details>

> 🤖 Generated by Coder Agents
2026-07-06 11:15:59 -04:00
Nick Vigilante 89b0a66079 docs: add top-level Get started section and move the Quickstart (#26821)
Add a top-level "Get started" docs section to the nav and move the Quickstart to /docs/get-started, with inbound link updates and the install page TIP pointing to the Quickstart.

Filed via Coder Agents on Nick's behalf.
2026-06-29 16:07:10 -04:00
Nick Vigilante ed908ed019 fix(docs): repoint 7 broken external and anchor links (DOCS-415) (#26572)
Closes [DOCS-415](https://linear.app/codercom/issue/DOCS-415).

## TL;DR

Repoints 7 broken links across 5 docs files that the 2026-06-22 weekly
`check-docs.yml` Linkspector run flagged. Two other links from the same
run (the dead `nix` ref and the dead `reflectoring.io` ref in
`CONTRIBUTING.md`) were already folded into
[#26341](https://github.com/coder/coder/pull/26341).

## Why

Broken external and anchor links degrade reader trust, leak SEO juice,
and make the docs look stale. The weekly `check-docs` job exists
precisely to catch this kind of rot before customers do; the
surfacing-to-fix turnaround on these 7 is one PR. Run that surfaced
them: [actions/runs/27948011619 job
82697664858](https://github.com/coder/coder/actions/runs/27948011619/job/82697664858).

## Scope

| File | Line(s) | Old target | New target | Why |
|------|---------|-----------|------------|-----|
| `docs/tutorials/best-practices/organizations.md` | 62 | anchor
`#update-template-metadata-by-id` | `#update-template-settings-by-id` |
API endpoint renamed in
[#19228](https://github.com/coder/coder/pull/19228) (Aug 2025). New
heading at line 1105 of `docs/reference/api/templates.md`. |
| `docs/install/registry-mirror-artifactory.md` | 197 | JFrog
`terraform-registry` |
`terraform-opentofu-and-terraform-backend-repositories` | JFrog
consolidated their Terraform / OpenTofu / Backend docs into a single
page. |
| `docs/admin/templates/extending-templates/modules.md` | 76, 206 |
JFrog `set-up-a-terraform-module/provider-registry` and
`terraform-registry` | same consolidated JFrog page (root, no anchor) |
Same JFrog consolidation. Anchor dropped, see decision log. |
| `docs/admin/integrations/dx-data-cloud.md` | 84 |
`https://help.getdx.com/en/` | `https://docs.getdx.com/` | DX migrated
their help center to a separate docs domain. |
| `docs/about/contributing/frontend.md` | 37, 71 |
`https://reactrouter.com/en/main` | `https://reactrouter.com/` | React
Router dropped the `/en/main` prefix. |

## Validation

- All 7 replacement URLs return HTTP 200 (manual `curl -L -o /dev/null
-w '%{http_code}'` per URL; linkspector's puppeteer crashed in the agent
env, so it was run case-by-case)
- `make lint/markdown lint/emdash` clean locally
- Pre-commit hook (`scripts/githooks/pre-commit` -> `make
pre-commit-light`) clean
- No `/docs/` route changes; pure markdown content

## Not triggering `/coder-agents-review`

Docs-only markdown edit, no CI or build config changes; per `AGENTS.md`
the bot review is reserved for product / CI changes. `doc-check` handles
this category.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Replacement URL also turns out to be broken later | All 7 verified
HTTP 200 today; next weekly `check-docs` run will catch any future
regression. |
| JFrog anchor drop on `modules.md` (76, 206) loses navigation context |
Verified the consolidated JFrog page has no clean section anchor for the
original target; linking the root page is the honest fix. If JFrog ships
a better TOC anchor later, a follow-up can reattach. |
| Anchor rename in `organizations.md` was actually a different rename |
Confirmed via PR #19228 (Aug 2025) which is the exact rename that
produced `## Update template settings by ID`. |

<details>
<summary>Decision log</summary>

**Why drop the anchor on the JFrog `modules.md` links (76 + 206)**:
JFrog's new consolidated page
(`/terraform-opentofu-and-terraform-backend-repositories`) doesn't
expose the original `set-up-a-terraform-module/provider-registry`
section as a fragment-link target. The honest fix is to link the page
root; readers can scroll. The `registry-mirror-artifactory.md:197`
reference uses the same root link for symmetry.

**Why DX `docs.getdx.com` over `help.getdx.com`**: DX's help center at
`help.getdx.com/en/` now returns 404. They moved to a separate
`docs.getdx.com` domain with a different content structure. Linking the
docs root is the closest analog to the original "browse our docs"
intent.

**Why React Router root over `/en/main`**: React Router unified their
docs under the root URL. The `/en/main` prefix is no longer routable.
The root URL is the canonical successor.

</details>


<details>
<summary>CI: <code>audit-docs-paths</code> failure (pre-existing,
unrelated)</summary>

The `audit-docs-paths` job in `.github/workflows/weekly-docs.yaml` fails
on this PR because its `Fetch redirects.json` step issues an
unauthenticated `curl` to a file in private `coder/coder.com` and gets a
404 (exit code 22). Same failure on every recent PR in this repo.
Tracked in [DOCS-409](https://linear.app/codercom/issue/DOCS-409) and
fixed in [#26571](https://github.com/coder/coder/pull/26571), which
authenticates the fetch through the Contents API. My changes are
docs-content only (5 markdown files, 7 line changes) and don't touch the
TS/TSX paths or `redirects.json` that the audit examines, so this is a
pre-existing CI break, not a regression introduced here.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*
2026-06-22 17:04:19 -04:00
Nick Vigilante e458692cb8 refactor(docs): convert absolute coder/coder blob/tree/main links to relative (DOCS-351) (#26341)
Closes [DOCS-351](https://linear.app/codercom/issue/DOCS-351).

> [!WARNING]
> **DO NOT MERGE** until
[DOCS-349](https://linear.app/codercom/issue/DOCS-349)
([coder.com#877](https://github.com/coder/coder.com/pull/877)) has
shipped to production and baked for at least one Vercel cycle.
>
> Without DOCS-349, the relative links in this PR resolve to broken
docs-route URLs (`/docs/helm/coder/values.yaml` -> 404) instead of
GitHub URLs tagged with the displayed docs version. DOCS-349 fixes the
rewriter to classify these as GitHub blob/tree URLs with the page's
resolved ref.

## TL;DR

Converts 121 absolute
`https://github.com/coder/coder/(blob|tree)/main/<path>` links across 39
docs markdown files to relative paths. After this lands AND DOCS-349
deploys, every one of these links will follow the displayed docs version
(mainline tag on bare URLs, explicit tag on `/@vX.Y.Z/`, `main` on
`/@main/`) instead of always pointing to `main`.

## Why

Today a reader on `/docs/@v2.30.0/install/docker` follows a
`compose.yaml` link and arrives at `main`'s `compose.yaml`, which
doesn't necessarily match what the docs page describes. Helm values,
Terraform templates, and source-code references in particular drift
across versions. The fix is to let the coder.com rewriter substitute the
page's resolved ref into the URL; that only works on relative links.

## Example payoff (post-DOCS-349)

| URL | Today (absolute, always `main`) | After (relative + rewriter) |
|---|---|---|
| `/docs/install/docker` |
`https://github.com/coder/coder/blob/main/compose.yaml` |
`https://github.com/coder/coder/blob/v2.34.1/compose.yaml` (today's
mainline) |
| `/docs/@v2.30.0/install/docker` | same as above |
`https://github.com/coder/coder/blob/v2.30.0/compose.yaml` |
| `/docs/@main/install/docker` | same as above |
`https://github.com/coder/coder/blob/main/compose.yaml` |

## Scope

- **121 conversions** across **39 files**.
- Verb breakdown: `tree/main` (directories) and `blob/main` (files),
both flipped to relative paths.
- Line anchors (`#L23-L24`) and query strings preserved verbatim.
- Conversion is mechanical: relative path computed from the doc file's
directory to the target via `os.path.relpath`. Any path starting at the
same directory or below gets a `./` prefix; otherwise `../` chains.

## Rebased on main

The branch was rebased onto `main` after the DOCS-350 hotfix
([#26339](https://github.com/coder/coder/pull/26339)) merged. The hotfix
repointed 3 `docs-backend-contrib-guide` refs in `backend.md` to `main`,
which then needed the same `main` -> relative conversion this PR is
doing for the other 121 links. The conflict was resolved by reapplying
the mechanical conversion to `backend.md` after taking the hotfix's
content. Net result: those 3 links land here as relative, same as
everything else. New HEAD `3f501cb622`.

## Inline fix folded in: dead `nix` link

- `docs/about/contributing/CONTRIBUTING.md:7` -> `../../../nix`

The original absolute URL `https://github.com/coder/coder/tree/main/nix`
already returned 404 today. Repointed to `flake.nix` (modern Nix
entrypoint, what the prose "Nix environment" semantically refers to).
Closes [DOCS-357](https://linear.app/codercom/issue/DOCS-357) here since
the `check-docs` Linkspector job surfaced it during rebase; cheaper to
fix inline than in a separate single-line PR.

## Out of scope (filed separately)

- [DOCS-350](https://linear.app/codercom/issue/DOCS-350): 3 dead
`docs-backend-contrib-guide` branch refs in `backend.md`
([#26339](https://github.com/coder/coder/pull/26339), merged).
- [DOCS-352](https://linear.app/codercom/issue/DOCS-352): 10 SHA-pinned
`(blob|tree)/<sha>` links pending intent review.
- [DOCS-355](https://linear.app/codercom/issue/DOCS-355): code-server
analog (4 absolute `(blob|tree)/main` links in `coder/code-server`).
- [DOCS-356](https://linear.app/codercom/issue/DOCS-356): 2 upstream
content bugs in `coder/code-server/docs/CONTRIBUTING.md` (independent of
this PR).


## Not triggering `/coder-agents-review`

Docs-only edit; per `AGENTS.md` the bot review is reserved for
product/CI changes.

## Pre-mortem

| Concern | Mitigation |
|---|---|
| Merging before DOCS-349 deploys regresses ~120 currently-working links
into 404s on coder.com | Clear DO-NOT-MERGE banner; tracked as blocker
in Linear. |
| Relative path computed incorrectly (off-by-one `..`) | Verified all
114 newly-relative non-md/non-image paths resolve to existing files in
the repo (only exception is the pre-existing dead `nix` link above). |
| Line anchors stripped during conversion | Preserved by the
substitution regex; verified `#L<n>-L<m>` cases in `airgap.md` and
`speed-up-templates.md`. |
| Future code reorgs change file locations | Relative links will start
pointing to nothing. Same failure mode as absolute links pointing to
renamed files; can be caught with a future link-checker job. |

## Validation

```
$ grep -rE 'github\.com/coder/coder/(blob|tree)/main' docs --include="*.md" | wc -l
0
$ git diff --stat origin/main | tail -1
39 files changed, 118 insertions(+), 118 deletions(-)
```

114 newly-relative paths verified to resolve to existing repo files
(Python `os.path.exists` check on each computed target).

<details>
<summary>Decision log + planning context</summary>

**Why relative over `(blob|tree)/{{currentDocsVersion}}/...`
templating**: relative paths require zero markdown-system support and
zero upstream churn beyond this one PR. Templating would require a
preprocessor on `coder.com` side AND a convention upstream authors have
to remember; relative paths just work in a plain editor and
`github.com`'s own renderer too.

**Why `./` prefix on same-directory targets**: makes the conversion
grep-able later (`grep -E '\((\.\./|\./)'`).

**Why preserve `#L<n>-L<m>` anchors verbatim**: the anchor is meaningful
to the linked file's content, not to the URL form; keeping it as-is
preserves authorial intent. If the file later changes such that the line
range drifts, that's a different problem the SHA-pin audit
([DOCS-352](https://linear.app/codercom/issue/DOCS-352)) will surface.

</details>

---

*Generated by Coder Agents on @nickvigilante's behalf.*





## Drive-by external link fix folded in

`docs/about/contributing/CONTRIBUTING.md:296` cited
`https://reflectoring.io/meaningful-commit-messages/` which is returning
HTTP 503 (the host appears to be down site-wide right now). `check-docs`
Linkspector flagged it after the rebase. Replaced with
`https://cbea.ms/git-commit/` (Chris Beams' canonical "If applied, this
commit will..." article, confirmed 200), which is the original source of
the rule the prose recites anyway.
2026-06-22 11:39:12 -04:00
Asher 9b847cc5ab feat: support "me" with shared_with_user filter (#26494) 2026-06-17 13:35:53 -08:00
Nick Vigilante 9d85eb2fa0 docs(docs/tutorials/quickstart.md): recommend free container runtimes besides Docker Desktop (#26106)
Replaces the quickstart's "Install Docker" step with a runtime-agnostic
"Install a container runtime" step, and switches the per-platform
defaults
to free, lightweight options that avoid Docker Desktop's cost and
overhead.

A callout above the OS tabs names Colima, Rancher Desktop, Podman, and
Docker Desktop as valid runtimes and tells readers to skip ahead if they
already have one running. The default install path per platform is now:

- Linux: Docker Engine (unchanged from the previous doc)
- macOS: Colima with the Docker CLI
- Windows: Podman Desktop

The "Cannot connect to the Docker daemon" troubleshooting subsections
are
updated to match the new defaults.

Closes
[DEVREL-22](https://linear.app/codercom/issue/DEVREL-22/recommend-orbstackcolimarancher-desktoppodman-as-docker-desktop).

A follow-up Linear issue will track a deeper "Docker runtime
alternatives"
reference page covering OrbStack (with its commercial-license caveat),
Rancher Desktop, and CLI Podman.

<details>
<summary>Implementation proposal and pre-mortem</summary>

# DEVREL-22 proposal: Replace "Install Docker" with "Install a container
runtime"

Linear:
[DEVREL-22](https://linear.app/codercom/issue/DEVREL-22/recommend-orbstackcolimarancher-desktoppodman-as-docker-desktop)
Repo: `coder/coder`
Branch:
`vigilante/devrel-22-recommend-orbstackcolimarancher-desktoppodman-as-docker`
Primary file: `docs/tutorials/quickstart.md`

## Summary

Rename Step 1 of the quickstart from "Install Docker and set up
permissions" to "Install a container runtime", add a callout that any
Docker-compatible runtime works, and switch the per-platform default to
the lightest free path on each OS. Keep Docker Desktop, OrbStack, and
Rancher Desktop as documented alternatives, but not as the primary
recommendation. The deeper "alternatives" reference page is a follow-up.

## Why

- Docker Desktop is slow on macOS/Windows and requires a paid license
for most commercial use.
- The Coder Quickstart template only needs the Docker daemon, not Docker
Desktop's GUI.
- No single tool satisfies "curl install + cross-platform + free +
minimal setup", so a per-platform recommendation is the honest answer.

## Per-platform defaults

| Platform | Default in quickstart | Why |
|----------|----------------------|-----|
| Linux | Docker Engine via `curl -sSL https://get.docker.com \| sh` |
Already in the doc, already a curl one-liner, already free. No change. |
| macOS | Colima | Two commands (`brew install colima docker`, `colima
start`), free for commercial use, exposes `/var/run/docker.sock` so the
Coder template needs zero env vars. |
| Windows | Podman Desktop | Free, handles WSL2 prereq and `podman
machine` setup through the GUI, sets up Docker socket compatibility.
Lighter than Docker Desktop, simpler than CLI Podman + `DOCKER_HOST`. |

## Pre-mortem

1. **Coder Quickstart template assumes `/var/run/docker.sock`.** Colima
symlinks it on macOS. Podman Desktop on Windows enables Docker socket
compatibility by default, so the template's Docker provider should reach
the daemon without `DOCKER_HOST` gymnastics.
2. **External links into
`quickstart#step-1-install-docker-and-set-up-permissions`.** A grep of
`docs/` and `site/` found no internal references to the old anchor. Blog
posts or external links may land at the top of the page after the
rename; acceptable for this scope.
3. **Brew assumption on macOS.** Recommending `brew install colima
docker` assumes Homebrew. The callout links to brew.sh so users without
it can install Homebrew first.
4. **WSL2 on Windows.** Podman Desktop's onboarding installs WSL2 if
missing. Corporate-managed machines that block WSL2 can fall back to
other runtimes named in the callout.
5. **Onboarding tone shift.** "Container runtime" is more abstract than
"Docker." The callout names Docker Desktop as a runtime first, so the
unfamiliar phrase is anchored immediately.
6. **OrbStack license trap.** OrbStack is intentionally not in the
quickstart's default path because it is paid for commercial use. It will
be mentioned on the future alternatives page with the license caveat
called out explicitly.

## Out of scope (follow-up issues)

- New "Docker runtime alternatives" reference page covering OrbStack,
Rancher Desktop, CLI Podman, with license and compatibility notes.
- `docs/install/docker.md` updates. That page is about installing Coder
server in a Docker container, which is a separate concern.
- Updating the `coder/skills` `setup` skill if its install steps drift
from the new quickstart.
- Updating the Coder Quickstart template's description in
`coder/registry` if it links to the renamed section.

</details>

This pull request was generated by a Coder agent on behalf of
@nickvigilante.
2026-06-05 22:03:51 +00:00
Nick VigilanteandClaude Opus 4.8 ca337915cc docs: fix broken and naked relative links (#25825)
Several relative links in the docs pointed at pages that no longer exist
or rendered incorrectly on coder.com.

Fixes:

- `start/first-template.md`: IDE links repointed from the removed
`../ides.md` / `../ides/web-ides.md` to their current homes under
`user-guides/workspace-access/`.
- `tutorials/example-guide.md`: contributing link repointed to
`../about/contributing/documentation.md`.
- `about/contributing/backend.md`: the `migrations/testdata/fixtures`
and `full_dumps` references (and the `000024_example.up.sql` example)
used relative paths that escape `docs/` and render as bogus
`/docs/coderd/...` routes on the site. Normalized to the canonical
`github.com/coder/coder/(blob|tree)/main/...` form already used by ~120
other source links in the docs.
- Normalized extensionless directory links (`ai-coder/ai-gateway`,
`user-guides/workspace-access`, `install`) to their `/index.md` targets
for consistency with the rest of the docs.

This class of bug is invisible to the local doc checks (`make
lint/markdown` / `pnpm check-docs` only run markdownlint + table
formatting); only CI's Linkspector job validates link targets. Found via
a relative-link audit while investigating the docs preview on #25816.

Source-link version-awareness (so older docs versions don't all point at
`main`) is tracked separately in DOCS-268 and will be handled in the
coder.com render layer.


Linear: DOCS-278

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 08:47:29 -04:00
Ben Potter 5e44c71305 docs: call out coder/skills setup skill on install and quickstart pages (#25194)
<!--

If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

-->
2026-05-12 12:36:00 -05:00
Nick Vigilante 369a191972 feat: add Quickstart template with language and IDE selection (#24904)
Add a new Quickstart starter template that lets users pick programming
languages, editors, and an optional Git repo to clone. The template uses
Docker under the hood but presents a developer-focused experience: pick
your tools, start coding.

## What's included

- **Languages parameter** (multi-select): Python, Node.js, Go, Rust,
Java, C/C++
- **IDEs parameter** (multi-select): VS Code (Browser), VS Code Desktop,
Cursor, JetBrains, Zed, Windsurf
- **Git repo parameter**: Optional URL to clone on workspace start
- **JetBrains filtering**: Maps selected languages to relevant IDE codes
(Python → PyCharm, Go → GoLand, etc.)
- **Docker precondition check**: Uses `data "external"` +
`terraform_data` precondition to surface a friendly error when Docker is
unavailable, before the Docker provider fails with a cryptic message
- **4 presets**: Web Development, Backend (Go), Data Science, Full Stack
- **Single install script**: All languages install in one `coder_script`
to avoid apt-get lock conflicts (agent scripts run in parallel via
`errgroup`)

<details><summary>Design decisions</summary>

- **Docker as invisible backend**: Docker is required on the Coder
server but never mentioned in the user-facing parameter UI. The
experience is entirely "pick languages, pick editors, start coding."
- **`coder_script` over startup_script**: Language installs use a
templated script file (`install-languages.sh.tftpl`) driven by the
languages parameter. A single script avoids dpkg lock contention since
`coder_script` resources execute concurrently.
- **`data "external"` for Docker check**: The external provider probes
Docker availability independently of the Docker provider. If Docker is
down, the `terraform_data` precondition fails with a human-readable
message before any `docker_*` resource is evaluated. This depends on the
Docker provider connecting lazily (at resource eval time, not at
provider init), which current behavior confirms.
- **JetBrains filtering by language**: Rather than showing all 9
JetBrains IDEs, the template computes relevant IDE codes from the
language selection (e.g. Python → PY, Go → GO) and passes them as
`default` to the JetBrains module.
- **Arch-aware Go install**: The install script detects `uname -m` to
download the correct Go binary for amd64 or arm64.

</details>

<details><summary>Screenshots and recordings from the UI</summary>
<p>
<img width="1851" height="1471" alt="Screenshot 2026-05-05 at 2 14
20 PM"
src="https://github.com/user-attachments/assets/d4c9cdc5-d311-43a5-9e2e-f90b0019eda7"
/>
<img width="1851" height="1471" alt="Screenshot 2026-05-05 at 2 15
06 PM"
src="https://github.com/user-attachments/assets/cf3023fe-b6db-4503-a6c4-eaa0ec0659f8"
/>


https://github.com/user-attachments/assets/7507fd7d-ddb5-457a-9f7d-cbf89b36eb20


</p>
</details> 

> [!NOTE]
> This PR was authored by Coder Agents.
2026-05-06 13:55:38 +00:00
Ben Potter cfce751b8a docs(docs): improve Docker daemon troubleshooting for all platforms (#24922)
Improves the Docker daemon troubleshooting in the quickstart and Docker
install docs:

- Renames the quickstart entry from "Cannot connect to the Docker daemon
on Linux" to cover all platforms.
- Adds a plain-English explanation of what the error means (Docker is
not installed or not running).
- Adds tabbed macOS/Linux/Windows instructions to the quickstart (macOS
and Windows were missing).
- Simplifies the Linux steps to match what Step 1 of the quickstart
already teaches.
- Adds a matching entry to `docs/install/docker.md` with a cross-link to
the quickstart for platform-specific steps.

Supersedes #24907 which was closed without merging.

Fixes https://linear.app/codercom/issue/DEVREL-23

> Generated with [Coder Agents](https://coder.com/agents)
2026-05-05 12:44:39 -05:00
david-fraley 98ea5266c3 docs: point to Coder Agents and drop Tasks walkthrough in quickstart (#24833) 2026-05-05 09:02:13 -04:00
97cc83c83e docs: reduce friction for first-time users (#24405)
Walking through the quickstart as a new user surfaced several small
friction points and formatting issues. This PR fixes the ones I hit:

- Split the combined Linux/macOS install tab — the steps are
meaningfully different (daemon vs Docker Desktop, package vs `.app`),
and collapsing them forced readers to mentally filter instructions.
- Added a "Launch the Docker daemon" step for Linux and "Open Docker
Desktop" for macOS/Windows. Without this, a new user who installs Docker
and proceeds to `coder server` hits the "Cannot connect to the Docker
daemon" error on first workspace build.
- Added "Familiarity with running commands in the terminal" to
Prerequisites to set expectations accurately.
 - Fixed "Setup" → "Set Up" (verb form) in the Step 1 heading.
 - Fixed "Congratulation" → "Congratulations".
- Corrected ```hcl` code fences to ```shell` on `git clone`, `cd`, and
`coder template push` commands — none of those are HCL.
- Added trailing punctuation to numbered-list items in the Tasks section
for consistency.
- Stripped Google Analytics session parameters (`_gl`, `_ga`, `_gcl_au`)
from two `registry.coder.com` URLs. These were copy-pasted from a
browser session and leak a GA session ID into public docs.
- Scoped the "Cannot connect to the Docker daemon" troubleshooting
heading to Linux, since it's specific to the Linux install path.
 - Minor copyediting for tone and clarity in the intro and overview.

<!--

If you have used AI to produce some or all of this PR, please ensure you
have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

-->

---------

Co-authored-by: Nick <nt@vigiemail.com>
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
Co-authored-by: Ben Potter <ben@coder.com>
Co-authored-by: Ben Potter <me@bpmct.net>
2026-04-30 17:12:25 +00:00
Sushant P 4820f13eb4 docs: add deprecation warning for login-type none (#24594)
The `--login-type none` option for `coder users create` is deprecated.
This adds deprecation warnings to all docs that reference it and updates
the CI/CD tutorial to recommend the replacement flows.

Refs DEVEX-224

<details>
<summary>Changes</summary>

- `cli/usercreate.go`: Append deprecation notice to `--login-type` flag
description.
- `docs/tutorials/testing-templates.md`: Replace `--login-type none`
example with separate Premium (`--service-account`) and OSS
(`--login-type password`) examples.
- `docs/reference/cli/users_create.md`: Regenerated from CLI source.
- `cli/testdata/coder_users_create_--help.golden`: Updated golden
snapshot.

</details>

> [!NOTE]
> Generated by Coder Agents.
2026-04-27 22:51:01 +00:00
DevCats b89ddb593d docs: add coder-templates skill references to quickstart and template contribution guides (#24383)
Adds TIP alerts to the quickstart guide and the template contribution
guide linking to the
[coder-templates](https://github.com/coder/registry/blob/main/.agents/skills/coder-templates/SKILL.md)
agent skill from the Coder Registry. This gives contributors and new
users a pointer to AI-assisted template authoring when they first
encounter template creation.
2026-04-16 12:04:30 -05:00
Sushant PandJiachen Jiang 61d6c728b9 docs: adding a tutorial for persistent shared workspaces (#23738)
Co-authored-by: Jiachen Jiang <jcjiang42@gmail.com>
2026-04-06 13:22:25 -07:00
Danny Kopping 1dd0519a38 docs: clarify max_connections implications (#21596)
Signed-off-by: Danny Kopping <danny@coder.com>
2026-01-21 22:10:12 +02:00
Danny Kopping 49a42eff5c feat: make database connection pool size configurable (#21403)
Closes https://github.com/coder/coder/issues/21360

A few considerations/notes:
- I've kept the number of conns to 10 in all other places, except coderd
- which uses the config value
- I opted to also make idle conns configurable; the greater the delta
between max open and max idle, the more connection churn
- Postgres maintains a [_process_ per
connection](https://www.postgresql.org/docs/current/connect-estab.html),
contrary to what the comment said previously
- Operators should be able to tune this, since process churn can
negatively affect OS scheduling
- I've set the value to `"auto"` by default so it's not another knob one
_has to_ twiddle, and sets max idle = max conns / 3

---------

Signed-off-by: Danny Kopping <danny@coder.com>
2026-01-13 10:50:57 +02:00
Rowan Smith 0ba3f7e9fd chore: update organizations.md for Terraform provider support (#21300) 2025-12-21 06:07:31 +11:00
Mathias Fredriksson dac822b7f4 refactor: remove deprecated AITaskPromptParameterName constant (#21023)
This removes the deprecated AITaskPromptParameterName constant and all
backward compatibility code that was added for v2.28.

- Remove AITaskPromptParameterName constant from codersdk/aitasks.go
- Remove backward compatibility code in coderd/aitasks.go that populated
  the "AI Prompt" parameter for templates that defined it
- Remove the backward compatibility test (OK AIPromptBackCompat)
- Update dbfake to no longer set the AI Prompt parameter
- Remove AITaskPromptParameterName from frontend TypeScript types
- Remove preset prompt read-only feature from TaskPrompt component
- Update docs to reflect that pre-2.28 definition is no longer supported

Task prompts are now exclusively stored in the tasks.prompt database
column, as introduced in the migration that added the tasks table.
2025-12-16 15:14:59 +00:00
Cian JohnstonandBen Potter b6935c3528 chore(docs/ai-coder): add migration guide for provider version 2.13.0 (#20426)
Closes https://github.com/coder/internal/issues/1080

---------

Co-authored-by: Ben Potter <ben@coder.com>
2025-11-11 09:07:41 +00:00
1d1e1f92b0 docs: add OIDC documentation for Microsoft Entra ID user auth (#20202)
Propose Microsoft Entra ID OIDC Directions for Admin Documentation based
on my personal experience / setup.

Propose information on changing access URL in Tutorials -> FAQs

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: DevCats <chris@dualriver.com>
Co-authored-by: DevelopmentCats <christofer@coder.com>
2025-11-07 16:33:51 +05:00
david-fraley cda859363e docs: fix formatting issues (#19831) 2025-09-15 17:02:56 -05:00
Rowan Smithanddavid-fraley f40ae708d6 chore: fix grammar issue (#19810)
fix a grammar issue.

Added an extra whitespace to the end / expanded the width of the table
to maintain space as adding the one char would have pushed `helps` hard
up against the table edge.

> If you have used AI to produce some or all of this PR, please ensure
you have read our [AI Contribution
guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING)
before submitting.

no AI

Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-09-15 21:33:56 +00:00
david-fraley 1e2b66fb20 docs: update Get Started Page to Include Tasks (#19752) 2025-09-12 22:22:28 +00:00
blink-so[bot]anddavid-fraley 02ecf32afe docs: replace offline deployments terminology to air-gapped (#19625)
This PR comprehensively updates the offline deployments documentation to
use more precise "air-gapped" terminology and improves consistency
throughout the documentation.

## Changes Made

### Terminology Updates
- **Title**: Changed from "Offline Deployments" to "Air-gapped
Deployments"
- **Summary**: Updated to prioritize "air-gapped" terminology and added
"disconnected" to cover additional deployment scenarios
- **Content**: Updated tutorial references to use "air-gapped" instead
of "offline"
- **Section headers**: 
  - Changed "Offline container images" to "Air-gapped container images"
  - Changed "Offline docs" to "Air-gapped docs"
- **Table headers**: Changed "Offline deployments" to "Air-gapped
deployments"

### Navigation & URL Structure
- **Navigation title**: Updated `docs/manifest.json` to show "Air-gapped
Deployments" in sidebar
- **Navigation description**: Updated to "Run Coder in air-gapped /
disconnected / offline environments"
- **File rename**: `docs/install/offline.md` → `docs/install/airgap.md`
for consistency
- **URL change**: `/install/offline` → `/install/airgap`
- **Subsection anchors**:
  - `/install/offline#offline-docs` → `/install/airgap#airgap-docs`
- `/install/offline#offline-container-images` →
`/install/airgap#airgap-container-images`

### Internal Links & References
Updated all internal documentation links:
- `docs/admin/integrations/index.md`
- `docs/admin/networking/index.md`
- `docs/changelogs/v0.27.0.md` (including anchor reference)
- `docs/tutorials/faqs.md`

### Backward Compatibility
- **Redirects**: Added `docs/_redirects` with 301 redirects:
  - `/install/offline` → `/install/airgap`
  - `/install/offline#offline-docs` → `/install/airgap#airgap-docs`
- `/install/offline#offline-container-images` →
`/install/airgap#airgap-container-images`
- **Content**: Maintains "offline" in the description for broader
understanding
- **Deep links**: All subsection anchors redirect properly to maintain
existing bookmarks

## Rationale

- **"Air-gapped"** is more precise and commonly used in
enterprise/security contexts
- **"Disconnected"** covers additional scenarios where networks may be
temporarily or partially isolated
- **Consistency** ensures filename, URL, navigation, content, and
subsection anchors all align with the same terminology
- **Backward compatibility** maintained through comprehensive redirects
to prevent broken links at any level

## Testing

- [x] Verified all internal links point to the new URL structure
- [x] Confirmed navigation title updates correctly
- [x] Ensured content accuracy is maintained
- [x] Added redirects for backward compatibility (main page +
subsections)
- [x] Updated all cross-references in related documentation
- [x] Verified subsection anchor redirects work properly
- [x] Confirmed no unnecessary .md file redirects

## Result

Complete terminology consistency across:
- ✅ Page title and headers
- ✅ Navigation and breadcrumbs  
- ✅ File names and URL structure
- ✅ Internal documentation links
- ✅ Table headers and section titles
- ✅ Subsection anchors and deep links
- ✅ Backward compatibility via comprehensive redirects

---------

Co-authored-by: blink-so[bot] <211532188+blink-so[bot]@users.noreply.github.com>
Co-authored-by: david-fraley <67079030+david-fraley@users.noreply.github.com>
2025-08-29 09:34:44 -05:00