Give an AI agent write access to content/ and it sounds like a contained permission. It isn't. MDX is not a document format, it is a JavaScript module with pleasant syntax. A file under content/ can import, call, and execute. "Only content" is a boundary drawn on a map, and the territory doesn't agree.

The content lockdown is what makes the boundary real. It is a remark plugin wired into the Velite pipeline, it runs over every content file at build time, and it fails the build on anything executable.

What it rejects

import { exec } from "node:child_process"     ← imports
<script>alert(1)</script>                     ← raw HTML elements
<Hero onClick={doThing} />                    ← event-handler props
<Hero title={process.env.SECRET} />           ← identifiers
<Hero href="javascript:alert(1)" />           ← unsafe URL schemes
<Card dangerouslySetInnerHTML={html} />       ← props that inject markup

What survives is a Block name, a prop name, and a literal value:

<Hero
  variant="centered"
  title="Your website, editable by any AI agent"
  primary={{ label: "Read the docs", href: "/about" }}
/>

Strings, numbers, booleans, arrays, objects, template literals with nothing interpolated into them, and nested Blocks. Nothing that runs.

Why it's an allowlist

A blocklist is a list of the attacks somebody thought of. Every syntax nobody anticipated walks straight past it.

The lockdown inverts that. It walks the syntax tree and rejects any node type it does not explicitly understand, so new MDX syntax, a new expression form, or an ECMAScript feature that ships next year all fail loudly on the next build rather than passing because nobody had added them to a list yet. Every other rule in the plugin follows from that one.

Where the second gate is

The lockdown proves content contains no code. It says nothing about whether the content is right: a Block still has to be handed the props it needs.

Zod does that job. Each Block validates its props when it renders, so the two gates catch different failures.

GateRuns atCatches
Content lockdownBuildExecutable code in a content file
Zod schemaRenderA Block given props it cannot use
Zod on YAMLBuildAn unsafe href in a settings file

That third row is easy to miss, so here it is plainly: YAML never passes through remark. The navigation, footer, and announcement files under content/settings/ are not MDX and the lockdown never sees them. Their href fields are checked by a Zod refinement using the same URL rule, and that refinement is the only thing between a settings edit and an unsafe URL in production. Do not replace it with a plain string field.

What it doesn't do

The lockdown is a content-integrity mechanism. It is not a permissions system, it is not a sandbox, and it has no opinion on who may edit what. Branch protection, review, and the platform runbooks in docs/platforms/ cover those.

What it does give you is narrower and more useful. Whatever an agent writes into content/, the worst case is bad copy, and bad copy arrives as a pull request that somebody reads.