# Settings

Every setting in sitemd lives in a markdown file with YAML frontmatter. Edit the file, save, and your site rebuilds with the new values.

```yaml
# settings/meta.md
---
title: My Site
url: https://example.com
description: A site built with sitemd
---
```

Settings files are in your `settings/` directory, tracked in git, and readable by humans and AI agents alike. The only exception is secrets (API keys, tokens, passwords) — those are stored separately in `.sitemd/config.json` and never committed.

## Settings files

| File | What it controls |
|---|---|
| `meta.md` | Site title, URL, description, favicon |
| `header.md` | Brand display, navigation items, search, theme toggle, auth button |
| `footer.md` | Footer links, copyright, tagline, social icons |
| `theme.md` | Color palette, dark/light mode, border radius, fonts |
| `groups.md` | Page groups for sidebars, header dropdowns, footer columns |
| `build.md` | Build pages list, output directory |
| `seo.md` | Language, OG images, structured data, robots, IndexNow |
| `deploy.md` | Domain, deploy target, provider settings, media hosting |
| `email.md` | Email provider, sender address, SMTP settings |
| `analytics.md` | Analytics provider, tracking ID, GTM, custom head |
| `auth.md` | Auth provider, login mode, redirects, provider URLs |
| `data.md` | Data provider, cache TTL, data sources, provider URLs |
| `forms.md` | Default submit label, thank-you message, honeypot |

The 9 core files (meta, header, footer, theme, groups, build, seo, deploy, data) ship with the scratch template. The remaining files (email, analytics, auth, forms) are created when you enable those features — via `sitemd config setup`, the MCP setup tools, or by creating the file manually. Run `sitemd init` or call [`sitemd_init`](/docs/mcp-server#sitemd_init) to scaffold a new project with the core files.

## How settings work

Every settings file follows the same pattern: YAML key-value pairs between `---` delimiters. Comments starting with `#` are documentation — the build engine ignores them.

```yaml
# settings/deploy.md
---
# Deploy target: cloudflare, github, netlify, vercel
target: cloudflare

# Cloudflare Pages project name
cloudflareProject: my-site

# ── Secrets (CLI-managed) ─────────────────────────
# Manage secrets: sitemd config setup deploy
# cloudflareApiToken: ...8759
---
```

Three kinds of lines:

1. **Active settings** — `key: value` lines that the build engine reads
2. **Comments** — lines starting with `#` that document what each setting does
3. **Commented-out settings** — `# key: value` lines showing available options with defaults

To enable a commented-out setting, remove the `#` and set your value.

## Two-tier architecture

Settings are split into two tiers based on sensitivity:

| | Settings files (`settings/*.md`) | Config store (`.sitemd/config.json`) |
|---|---|---|
| **Contains** | Everything you'd put in source control | API keys, tokens, passwords |
| **Edited by** | You, directly in the file | `sitemd config set` or `sitemd config setup` |
| **Tracked in git** | Yes | No (gitignored) |
| **Examples** | Provider names, project IDs, URLs, domains, UI preferences | Cloudflare API token, email API key, Supabase anon key |

**The rule:** if it's safe to commit to git, it belongs in a settings file. If it's a secret, it belongs in the config store.

### The secrets mirror block

Settings files that have associated secrets show a mirror block at the bottom:

```yaml
# ── Secrets (CLI-managed) ─────────────────────────
# Manage secrets: sitemd config setup deploy
# cloudflareApiToken: ...8759
```

This block is **read-only** — the build engine writes it on every build to show which secrets are configured. The values are redacted (only the last 4 characters shown). To change a secret, run the command shown in the block or use `sitemd config set`.

Edit anything **above** the mirror block. The mirror block itself is overwritten on every build.

## Build precedence

When the same value exists in multiple places:

1. **Environment variables** — highest precedence (for CI)
2. **Config store secrets** — `.sitemd/config.json`
3. **Settings files** — `settings/*.md` frontmatter

In practice, you edit settings files for day-to-day changes, use the config store for secrets, and use environment variables only in CI pipelines.

{#settings-schema}

## Settings schema

Every settings file has a defined schema of valid keys. The schema is centralized in `settings-schema.js` — a single source of truth used by the build validator, MCP tools, and agents.

Each file entry in the schema defines:

- **required** — keys that must be present (e.g. `title` in `meta.md`)
- **known** — all valid keys for that file
- **validValues** — constrained options for specific keys (e.g. `target` in `deploy.md` must be one of `cloudflare`, `github`, `netlify`, `vercel`)

### Retrieving the schema

Agents retrieve the full annotated schema by reading `settings/_schema` (or calling the MCP settings tool with `name: "_schema"`):

```json
{ "name": "_schema" }
```

The response includes a `_guide` explaining the two-tier pattern, then every settings file with per-key metadata:

```json
{
  "_guide": "Settings live in two places: settings/*.md for non-secret config...",
  "deploy.md": {
    "required": [],
    "keys": {
      "target": { "validValues": ["cloudflare", "github", "netlify", "vercel"] },
      "cloudflareProject": { "label": "Cloudflare Pages project name", "secret": false },
      "cloudflareApiToken": {
        "label": "Cloudflare API token",
        "secret": true,
        "setVia": "sitemd_config_set",
        "configKey": "deploy.cloudflareApiToken"
      }
    }
  }
}
```

Each key includes:

| Field | Meaning |
|---|---|
| `label` | Human-readable description |
| `secret` | `true` if the value is a secret — must use `sitemd_config_set`, never write to settings files |
| `setVia` | Which tool to use (`sitemd_config_set` for secrets) |
| `configKey` | The dot-path key to pass to `sitemd_config_set` (included when it differs from the obvious `file.key` pattern) |
| `validValues` | Array of allowed values for constrained keys |

Keys with an empty object `{}` are simple non-secret settings — edit them directly in the settings file.

### Key reference

| File | Required | Known keys |
|---|---|---|
| `meta.md` | `title` | `title`, `brandName`, `description`, `url`, `brandImage`, `favicon`, `customFavicon`, `titleSuffix`, `tabTitle`, `tabTitleSuffix` |
| `header.md` | — | `brandDisplay`, `brandName`, `themeModeToggle`, `search`, `headerAuth`, `items` |
| `footer.md` | — | `copyright`, `brandName`, `brandDisplay`, `tagline`, `items`, `social` |
| `theme.md` | — | `defaultMode`, `accentColor`, `fontSans`, `fontMono`, `contentWidth`, `pageWidth`, `radius`, `imageCorners`, `borderRadius`, `buttonStyle` |
| `build.md` | — | `port`, `pagesDir`, `themeDir`, `outputDir`, `sitemap`, `robots`, `imageOptimization`, `imageMaxWidth`, `imageQuality` |
| `deploy.md` | — | `target`, `cloudflareProject`, `cloudflareAccountId`, `cloudflareBranch`, `netlifySiteId`, `vercelProjectId`, `vercelTeamId`, `githubRepo`, `githubBranch`, `mediaHosting`, `mediaCdnUrl`, `r2AccountId`, `r2Bucket`, `s3Region`, `s3Bucket`, `deployPages`, `additionalDomains` |
| `seo.md` | — | `language`, `ogImage`, `ogStyle`, `ogBackground`, `ogTextColor`, `ogLogo`, `defaultAuthor`, `twitterHandle`, `structuredData`, `llmsTxt`, `markdownOutput`, `allowAICrawlers`, `indexNow`, `orgName`, `orgLogo` |
| `email.md` | — | `provider`, `from`, `host`, `port`, `user`, `region` |
| `analytics.md` | — | `provider`, `id`, `host`, `gtm`, `pixels`, `customHead` |
| `auth.md` | — | `provider`, `loginMode`, `loginPage`, `afterLogin`, `afterLogout`, `userDataUrl`, `userTypeField`, `accessDeniedPage`, `apiUrl`, `supabaseUrl`, `firebaseAuthDomain`, `firebaseProjectId`, `auth0Domain`, `auth0ClientId` |
| `data.md` | — | `provider`, `cacheTTL`, `loadingText`, `emptyText`, `errorText`, `supabaseUrl`, `firebaseProjectId`, `airtableBaseId`, `restBaseUrl`, `sources` |
| `forms.md` | — | `submitLabel`, `thankYou`, `honeypot`, `countrySortToTop` |
| `groups.md` | — | `groups` |

Keys starting with `palette.` are always valid in any file — they define custom named colors.

## Self-repair

The build engine validates your settings files against the schema on every build. Warnings are non-blocking — your site still builds. They surface three types of issues:

### Missing required keys

```text
⚠ meta.md: missing required "title"
```

The key is required but not present in the file. Add it.

### Unknown keys

```text
⚠ meta.md: unknown key "typo" — check spelling or remove
```

The key isn't recognized in any settings file. Usually a typo.

### Misplaced keys

```text
⚠ meta.md: "items" belongs in header.md, not here
```

The key is valid but in the wrong file. The validator cross-references the schema to tell you exactly where it should go. Move it to the indicated file.

### Invalid values

```text
⚠ deploy.md: "target" has invalid value "clouflare" (expected: cloudflare, github, netlify, vercel)
```

The key has a value that doesn't match the allowed options. Fix the typo or pick from the listed values.

### Auto-scaffolding

Beyond settings validation, the build engine also auto-scaffolds missing pages referenced in navigation. If your `header.md` lists a nav item pointing to `/about` but `pages/about.md` doesn't exist, the build creates a placeholder page with the right frontmatter and a content template. This means you can define your navigation structure first, then fill in the content — the build won't break on missing pages.

## Editing settings via CLI or MCP

You don't have to edit files by hand. The [CLI](/docs/cli-config) and [MCP server](/docs/mcp-server) both route values to the right place automatically:

```bash
# Sets a secret → goes to .sitemd/config.json
sitemd config set deploy.cloudflareApiToken cf_xxx

# Sets a non-secret → goes to settings/deploy.md frontmatter
sitemd config set deploy.cloudflareProject my-site
```

The `sitemd config setup` wizard does this routing automatically — it asks for values, then writes non-secrets to settings files and secrets to the config store.

AI agents do the same thing through MCP:

```json
// Agent calls sitemd_config_set
{ "key": "deploy.cloudflareProject", "value": "my-site" }
// → Written to settings/deploy.md frontmatter

{ "key": "deploy.cloudflareApiToken", "value": "cf_xxx" }
// → Written to .sitemd/config.json
```

## CI environment variables

For CI pipelines, set environment variables instead of editing files. Environment variables take the highest precedence and override both settings files and the config store. See [CLI — CI environment variables](/docs/cli-config#ci-environment-variables) for the full mapping.

## Resetting a settings file

If a settings file gets corrupted or you want to start over, delete it and run `sitemd init` (or call `sitemd_init` via MCP) to copy the default from the scratch template. The build engine handles missing settings files gracefully — it uses built-in defaults for anything not specified.

## Related

- [CLI](/docs/cli-config) — manage secrets and run interactive setup
- [MCP Server](/docs/mcp-server) — programmatic access to settings, including schema retrieval
- [Build Modes](/docs/build-modes) — how trial (unactivated) and activated modes affect builds
- [Project Structure](/docs/project-structure) — where settings fit in the directory layout
- [Site Identity](/docs/site-identity) — meta.md deep dive
- [Themes](/docs/themes) — theme.md deep dive