# Configuration

Source: https://docs.agentblog.dev/reference/configuration
Summary: Every field in agentblog.config.ts, its type, its default, and what breaks when it is wrong.



`agentblog.config.ts` sits at your project root, next to `package.json` and
`next.config.ts`, including in a `src/` layout.

Exactly one module reads it: `lib/config.ts`. That is enforced by `agentblog
doctor`, because `@/` maps to the root in a flat layout and to `src/` in a
`src/` layout, so one import specifier cannot be right in both. With a single
importer, fixing a `src/` project is a one-line change rather than a search
across forty files.

## The shape [#the-shape]

```ts title="agentblog.config.ts"
import { defineConfig } from '@/lib/define-config'
import { mdxSource } from '@/lib/sources/mdx'

export default defineConfig({
  siteUrl: 'https://yourdomain.com',
  brand: {
    name: 'Your Brand',
    logo: { url: '/logo.png', width: 512, height: 512 },
    sameAs: ['https://github.com/yourbrand'],
  },
  source: mdxSource({ dir: 'content/blog' }),
})
```

`siteUrl`, `brand`, and `source` are required. Everything else has a default.

## Every field [#every-field]

| Field              | Type                                   | Default              | Notes                                                                              |
| ------------------ | -------------------------------------- | -------------------- | ---------------------------------------------------------------------------------- |
| `siteUrl`          | `string`                               | required             | https, no trailing slash, no path. Trailing slashes are stripped before validation |
| `locale`           | `string`                               | `'en_US'`            | Emitted as `og:locale` and as `inLanguage` in the structured data                  |
| `brand.name`       | `string`                               | required             | Becomes `og:site_name` and `Organization.name`                                     |
| `brand.logo`       | `{ url, width, height }`               | required             | `width` and `height` are required, not optional                                    |
| `brand.sameAs`     | `string[]`                             | `[]`                 | Absolute URLs only. A bare handle is rejected at build time                        |
| `source`           | `ContentSource`                        | required             | See [content sources](/reference/content-sources)                                  |
| `deployHook`       | `string`                               | none                 | Required, at compile time, when the source needs a rebuild to publish              |
| `revalidate`       | `number`                               | `3600`               | Revalidation window in seconds for the blog routes                                 |
| `postsPerPage`     | `number`                               | `12`                 | Index pagination                                                                   |
| `noindexTagsBelow` | `number`                               | `5`                  | Tag pages below this post count are marked `noindex`                               |
| `indexnow`         | `{ enabled, key? }`                    | `{ enabled: false }` | The key is 8 to 128 characters                                                     |
| `verification`     | `{ google?, yandex?, yahoo?, other? }` | `{}`                 | Maps straight onto the Next.js metadata verification fields                        |
| `aiAccess`         | `{ train?, search?, agent? }`          | all `true`           | Emitted into `robots.txt`                                                          |
| `preflight`        | `boolean`                              | `true`               | Build-time config linting                                                          |
| `trailingSlash`    | `boolean`                              | `false`              | Must match `next.config.ts`                                                        |
| `defaultAuthor`    | `string`                               | none                 | Author slug used when a post omits one                                             |

Unknown keys are ignored rather than rejected, so an invented field is silently
dead. Change values, not structure.

## The fields worth understanding [#the-fields-worth-understanding]

### `siteUrl` [#siteurl]

Every canonical link, sitemap entry, RSS link, and structured data identifier is
composed from this string, so a wrong value is wrong in about forty places at
once. Point it at the domain readers actually visit, never at a preview
deployment.

A trailing slash would produce `https://site.dev//blog/post` in every canonical,
so it is stripped and the value is revalidated before use.

Never build a URL by concatenating onto `config.siteUrl`. Use `absoluteUrl`,
`postUrl`, `categoryUrl`, `tagUrl`, and `authorUrl` from `lib/config.ts`. They
are the only code that knows your trailing-slash policy and the rule that a path
ending in a file extension never takes one.

### `brand.sameAs` [#brandsameas]

The single most valuable property in the file. It becomes `Organization.sameAs`,
which is how a search engine or an assistant resolves "the company that
published this" to a real entity rather than to a string that looks like a
company name. Entity resolution is what lets a model cite you by name and
attribute the claim correctly.

List profiles a third party can verify: LinkedIn, Crunchbase, GitHub, YouTube,
Wikidata. A bare handle is rejected, because it disambiguates nothing.

### `brand.logo` [#brandlogo]

`width` and `height` are required because Google's Organization markup needs
both, and a logo without dimensions is one of the most common structured data
errors on the web. A relative `url` resolves against `siteUrl`.

### `deployHook` [#deployhook]

Required when your content source declares that publishing needs a rebuild. Set
it to a rebuild trigger URL so the publish webhook can rebuild before pinging
IndexNow. In the other order you are telling a crawler to fetch a URL that was
never built.

You will not forget this one: the file stops type checking until it is set.

### `noindexTagsBelow` [#noindextagsbelow]

Thin tag pages are the classic way a blog generates hundreds of near-empty
indexable URLs and spends its own crawl budget on them. Categories are always
indexable. Tags have to earn it. Set it to `0` to index every tag.

### `indexnow` [#indexnow]

```ts
indexnow: { enabled: true, key: process.env.INDEXNOW_KEY }
```

The key is read from the config first and falls back to `INDEXNOW_KEY` in the
environment, so the secret never has to live in this committed file.

The matching `<key>.txt` file must be served from your domain root, UTF-8,
containing the key and nothing else, or every submission comes back 403. A key
hosted at a subpath only authorises URLs under that subpath. `agentblog doctor --fix` writes the file for you.

The limit is 10,000 URLs per request, not per day.

### `aiAccess` [#aiaccess]

```ts
aiAccess: { train: true, search: true, agent: true }
```

A forward-looking seam. Three standards are converging on machine-readable AI
usage preferences, and all of them attach to `robots.txt` or to HTTP headers.
None is implemented yet. When one lands, this same object emits it with no
change to your config file.

Setting `train: false` is a real tradeoff rather than a free one. Several
crawlers are multi-purpose, so opting out of training can also opt you out of
the search index that would have cited you.

### `preflight` [#preflight]

On by default. It reads your `next.config` off disk once per process during dev
and build and warns when a required setting is missing or narrowed. It never
throws and it does nothing at request time.

Set it to `false` only once you know your config is correct and you want the
warning gone. That is the supported way to switch it off. Deleting the preflight
import from `app/blog/layout.tsx` is not, because then nothing tells you when a
later edit breaks the config again.

### `trailingSlash` [#trailingslash]

Must match `trailingSlash` in `next.config.ts`. The URL helpers compose
canonicals from this value, so two different answers means every canonical
points at a URL that immediately redirects to its other form. Search engines
treat that as a redirect chain and AI crawlers frequently do not follow it at
all.

## Why `defineConfig` rather than `satisfies` [#why-defineconfig-rather-than-satisfies]

`defineConfig` is an identity function that exists for its type signature. It
gives contextual inference, which is what lets the `deployHook` requirement
depend on which content source you passed. `satisfies` checks an object against
a fixed type and cannot do that.

Concretely: swap `mdxSource` for a source that needs a rebuild to publish, and
the file stops type checking until you supply a `deployHook`. A runtime surprise
becomes a compile error, which is the highest-value thing the type layer does
here.

## Validation [#validation]

Your config is validated once, when `lib/config.ts` is first imported. A bad
value fails the build with a message naming the key, rather than producing
`undefined` inside a structured data graph three files later.
