agentblog.dev/docs

Configuration

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

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

FieldTypeDefaultNotes
siteUrlstringrequiredhttps, no trailing slash, no path. Trailing slashes are stripped before validation
localestring'en_US'Emitted as og:locale and as inLanguage in the structured data
brand.namestringrequiredBecomes og:site_name and Organization.name
brand.logo{ url, width, height }requiredwidth and height are required, not optional
brand.sameAsstring[][]Absolute URLs only. A bare handle is rejected at build time
sourceContentSourcerequiredSee content sources
deployHookstringnoneRequired, at compile time, when the source needs a rebuild to publish
revalidatenumber3600Revalidation window in seconds for the blog routes
postsPerPagenumber12Index pagination
noindexTagsBelownumber5Tag 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 trueEmitted into robots.txt
preflightbooleantrueBuild-time config linting
trailingSlashbooleanfalseMust match next.config.ts
defaultAuthorstringnoneAuthor 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

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

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.

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

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

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: { 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: { 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

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

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

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

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.

On this page