# Settings

> Seven stored preferences, four SEO readouts and one row that writes a commit — every setting on the screen, what reads it, and what it costs to get wrong.

Source: https://editor.astrocraftthemes.com/docs/settings/

`/admin/settings/` is **admin only**, because every row on it changes the deployment for everybody. It saves with JavaScript disabled, which is worth knowing if you ever debug it.

Seven values are stored, in one record in the same store your accounts live in — wherever your database URL puts it. Everything else on the screen is a readout of something the build already decided.

## Project

**Language** — the dashboard's language, for everyone who signs in. A searchable select over the nineteen shipped locales. The filter matches **endonyms** only, which is the honest limit: type "Korean" and you will not find 한국어. Labels stay in their own language, because a picker lost in the wrong language must still be readable.

**Folder** — a readout of the repository the CMS is editing.

**Git remote** — a readout, derived from the checkout with `git remote get-url` rather than restated in config. In a development tree it prints your real origin, which is deliberately the safety line for anybody about to press Publish.

## Publish

**Branch** — where Publish pushes. A combo box: known branches are suggestions, and a name nothing lists yet is legal. Typed names are validated by `git check-ref-format`, not by a regex, stored immediately, and created on the remote by your next publish. The list's last row is a **New branch** action.

This overrides `git.branch` in `src/admin.config.ts`, which is only the default. Publish reads the store, never the config.

**Commit messages** — the template each save names its commit with. `<collection>` and `<ids>` fill from the entry. The default reproduces `content(blog): update "my-post"` exactly. Also a combo, so the house templates are suggestions and your own wording is a value rather than a feature request.

**Push after commit** — off leaves commits local until you push. It defaults **off in a development tree**, so a save on your laptop cannot reach the real remote by surprise; the row's note says which way it landed.

## Preview and build

**Staging preview** — where your staging branch's deploy lives. Empty **hides** the _Preview your changes_ affordance on the review screen and in the editor, because a branch deploy's URL is platform-specific and only you know it.

**Build command** — run before every publish, and it _is_ the gate. A failing build refuses the push.

**Build timeout** — a hanging build must not hold a publish open.

## General SEO

Four readouts, zero stored fields. These files regenerate on every build, so there is no state a running server could change — a stored "enabled" flag here would be a switch wired to nothing.

| Row            | What it is                                                                                                   |
| :------------- | :----------------------------------------------------------------------------------------------------------- |
| **Sitemap**    | `/sitemap-index.xml` — regenerated from your published pages. Submit this URL to Search Console              |
| **robots.txt** | Points crawlers at the sitemap and keeps them out of the CMS. The staging build serves `Disallow: /` instead |
| **llms.txt**   | Your site's index for AI assistants, rebuilt from published entries                                          |
| **RSS feed**   | Published entries newest first, linked from every page's head                                                |

## Site — the row that writes a commit

**Domain** is the one control on this screen with no store key, and the reason is worth understanding.

Your site's URL is baked in at build time, so the host cannot be a runtime value — it has to be a committed **file**. Pressing **Connect** validates the host, rewrites `src/site.domain.ts`, and lands that as a commit. Your next build is what makes the new domain true.

Consequences:

- The row reads the value **off disk**, not out of the running server's bundle. A running server keeps its build's host until the next deploy, and the note names both during that window.
- The validation is stricter than the config check: it refuses `localhost`, address literals and LAN suffixes, because that value becomes a server-side fetch as well as a TypeScript string literal.
- The write is **surgical** — one regex over the export line — so a reformatted `site.domain.ts` throws at save rather than saving nothing quietly.

The Site group is not a settings row: **Save changes** does not write it, it posts to its own endpoint and answers with its own sentence.

## Licence

Paste your key, press Activate. Covered in [Licence and activation](/docs/licence/).

## Disconnect

Also not a settings row — an action with its own confirmation.

## What is deliberately not here

Two rows describing a managed preview server — a dev-server port and a stop-when-idle timeout — were **deleted** because nothing read them. A settings screen promising behaviour no code has is the same drift defect this product exists to avoid, arriving through a form. The dev server you already run plus your staging deploy cover both questions.

## Troubleshooting

**Save changes did nothing for the Domain row.** Correct — it has its own button. That is by design, since it writes a commit rather than a preference.

**A branch name is refused.** `git check-ref-format` rejected it. The message is git's own rule, not ours.

**Preview your changes is missing.** The staging preview URL is empty.

**The domain note says a change is pending after a deploy.** The committed file and the running server's bundle disagree until the server is rebuilt from that commit. That is the window the note exists to describe.

**Settings look reset after an update.** The stored record merges **per field**, so a record written by an older release cannot reset every setting. Genuinely lost settings mean a lost store — see [Bring a database](/docs/database/) for making that survivable.