# Articles

> The SEO app writes pages into your repository as files, and your own site builds them — what it does, what it does not, and how the wiring goes.

You have a site. You want pages on it that a search engine finds and a person
reads to the end. The SEO app is where those pages are written, and where they
are written **to** is your repository.

## What it does, and where it stops

An article is a file: `.beafk/seo/<collection>/<slug>.mdx`, in the project the
panel is standing in. A collection is a folder in there — `blog`, `changelog`,
`compare` — and it is committed with the rest of your work, reviewed the way
your code is reviewed, and deployed by whatever already deploys your site.

**beafk serves nothing.** There is no CDN of ours and nothing of ours runs when
a reader arrives. beafk writes the file; your build turns it into a page; your
host puts it on the web. If your site deploys from a push, nothing is on the
web until somebody pushes.

**beafk writes nothing either.** No model of ours runs on your articles. The
agent on your machine — Claude Code, Codex, Grok Build, Kimi Code, on the
subscription you already pay for — is the writer, and the app is where you ask
it and where you read what it wrote.

## The file

Five lines of front matter, then the page:

```yaml
---
title: Nothing is deleted — a thirty day drawer instead
description: One sentence, under 160 characters, that a result will show.
tags: [trash, design]
cover: /covers/piece-a1b2c3.png
coverAlt: the trash screen, thirty days of it
---
```

The editor draws its fields from those five and nothing else. The
`description` is what a search result shows and it is cut at 160 characters;
the editor draws a line there. The `cover` is the URL a reader's browser will
fetch — never a path relative to the file, because that previews perfectly and
ships a broken image.

The body is markdown, and it may use your site's own components as tags —
`<Callout>`, `<Figure>` — without importing them. Your site supplies them by
name when it renders. Keep the body to markdown and tags: an `export` or a
`{rows.map(…)}` compiles on Astro and Next and prints as text on Hugo,
Eleventy and SvelteKit.

## Making your site read it

Your framework has to be pointed at the folder once, per collection. Every
framework can be: Astro's `glob` loader takes a `base`, Hugo takes a module
mount, Nuxt Content takes a `cwd`, Next reads the folder with
`next-mdx-remote`, Eleventy takes a symlink or a virtual template. That is a
job for the agent, and the page it reads first is the **setup-articles**
skill — it is on every machine, and it is what an agent given "the site does
not show my articles" opens before touching a config file.

Two things it will need from you:

- **The site's public address.** `og:image` and the canonical link have to be
  absolute, and a build that does not know its own origin ships
  `http://localhost:3000` in the head of every page. It is `site` in Astro,
  `metadataBase` in Next, `baseURL` in Hugo. Have it in the config, or say it.
- **Where the pictures go.** A cover's bytes must be somewhere the site
  serves. Either the folder your build already serves — `public/`, `static/` —
  or a bucket you own (S3, R2, Backblaze, MinIO), where the object is public
  and the URL is permanent. A `covers/` folder beside the articles is not
  served by anything until it is copied or mounted.

Then the check that matters is not the build's exit code. It is the built HTML
of one article: the six head tags, the cover the head names present in the
output, and no component printed as text.

## What "published" means

A draft is a file whose name starts with `_`. Every loader the skill sets up
skips it, so a draft stays in the folder, in the repository, and off the site.

Publishing is the person's press, not the agent's. An agent asked to write a
page writes the file and says it is ready; committing it and pushing it are
yours, and so is renaming a slug — a slug is a live URL, and a rename without a
redirect is a page that was indexed and is now a 404.

## What it cannot do yet

As of September 2026 the app reads and writes your repository through the
machine: the library lists the collections and articles that are really in
`.beafk/seo`, the editor saves the file (and refuses to write over a version an
agent changed under it), and the publish switch renames a draft and makes one
scoped commit of that one file. Two things are still yours to do by hand:

- **Bucket credentials are not stored.** The storage screen keeps the bucket's
  endpoint, name and prefix, and the folder the site serves pictures from, but
  no key. Put a cover's bytes where the site serves them, or upload them with
  your own tooling, and write the URL into the front matter.
- **Nothing pushes.** A publish is a commit in your repository; the push, and
  the build that follows it, are your pipeline's.

The two skills, **write-an-article** and **setup-articles**, were tested on
Astro, Next, Hugo, Eleventy, SvelteKit and Nuxt with four different agents.

---

beafk documentation · Articles · https://beafk.app/docs/articles

Every page, one line each: https://beafk.app/docs/llms.txt
All of it in one file: https://beafk.app/docs/llms-full.txt
