Archival Editor Configuration

The optional archival_editor.toml file lets you customize how your site's content appears in the Archival editor.

This file is most useful when creating an archival template or for customizing the editing experience in the archival editor.

Place archival_editor.toml in the root of your repo, alongside archival_objects.toml and archival.toml. The editor reads it automatically when your site loads, and re-reads it whenever the file changes.

It is also where you turn on ActivityPub, which publishes your content to Mastodon and the rest of the fediverse whenever your site deploys, and Webmention, which lets your pages and pages on other sites tell each other when one links to the other.

Object views

The file configures views for your object types. A view controls which fields are shown as columns when you browse the list of objects of a given type in the editor.

The top-level key of each entry is the name of an object type, exactly as it appears in archival_objects.toml. Under each object type you define one or more views as a TOML array of tables:

# Configure views for the "post" object type
[[post.views]]
name = "default"
primary = "title"
secondary = "published_at"
tertiary = "author"

[[post.views]]
name = "by date"
primary = "published_at"
secondary = "title"

Each view supports the following fields:

  • name (required) — the label for this view. It appears in the view selector dropdown above the object list.
  • primary (required) — the field shown in the first column of the list.
  • secondary (optional) — the field shown in the second column.
  • tertiary (optional) — the field shown in the third column.

The values of primary, secondary, and tertiary are field names defined for that object type in archival_objects.toml. You can also use the special value filename, which displays the object's filename (its slug) rather than one of its fields.

How views are displayed

  • The first view listed for an object type is selected by default when you open that object's list.
  • The editor always appends a built-in slug view that shows only the filename column, so you can switch back to it at any time from the view selector.
  • If an object type has no entry in archival_editor.toml, its list shows a single filename column.
  • Around the columns you configure, every row also includes the object's order (always first) and an actions column (always last).

Example

Given an object type defined in archival_objects.toml:

[post]
title = "string"
author = "string"
published_at = "date"
body = "markdown"

This archival_editor.toml gives editors two ways to browse posts — one organized around the title, and one organized around the publish date:

[[post.views]]
name = "default"
primary = "title"
secondary = "author"
tertiary = "published_at"

[[post.views]]
name = "by date"
primary = "published_at"
secondary = "title"

See Object Fields for the field types you can reference, Validators for constraining what they may contain, and Custom Editors for customizing how individual fields are edited.

Shortcuts

A shortcut puts one list a single tap away from anywhere in the editor. Name the lists that get added to often — posts, updates, photos — and every editor screen grows a button in its bottom right corner that opens a composer for a new entry, with a publish button that writes and deploys it in one step.

Shortcuts are an array of tables at the top level of the file, alongside your object types:

[[shortcuts]]
name = "New Post"
path = "post"

[[shortcuts]]
name = "Site Update"
path = "homepage.updates"

Each entry takes:

  • path (required) — the list a new entry joins. See addressing a list, below.
  • name (optional) — the label on the button and in the shortcut menu. Left out, the last segment of path is used with dashes and underscores turned into spaces and each word capitalized, so post reads "Post" and press_releases reads "Press Releases".

shortcuts is reserved at the top level of this file, so an object type named shortcuts cannot be configured here.

Addressing a list

path addresses a list the way the editor addresses one everywhere else: the object type, then the filename when that type holds many objects, then the child lists to descend into, with the index of the entry you descend through between them. Segments are separated by dots, and an index counts from zero.

[[shortcuts]]
path = "post"                              # a new post

[[shortcuts]]
path = "homepage.updates"                  # a new update on the homepage

[[shortcuts]]
path = "post.hello-world.sections"         # a new section on the "hello-world" post

[[shortcuts]]
path = "post.hello-world.sections.0.links" # a new link on that post's first section

An object your site has exactly one of stores its content in a file named after the type, so there is no filename to give: homepage.updates, not homepage.homepage.updates. Pointing a shortcut at such an object on its own is refused, since it already holds the one file it will ever have.

Every step is checked against what your site actually contains. A type you have since removed, an index past the end of a list, or a path that stops at a single entry rather than a list resolves to nothing, and that shortcut simply does not appear. Correcting the path brings it back, along with any drafts saved under it.

Drafts

The composer saves what you write as you write it. A draft is kept on your device until you publish or discard it, so closing the editor and coming back tomorrow reopens it exactly as you left it.

Drafts are never written into your site's files. An unpublished draft is not a pending change, it does not show up in your unsynced changes, and publishing your site any other way leaves it alone.

The shortcut button carries a badge counting the drafts you have saved. Pressing it opens a menu of every shortcut you configured and every draft you have going, so you can start something new or pick up where you left off. With a single shortcut configured and nothing saved, pressing the button skips the menu and starts a draft immediately.

Inside the composer:

  • Save closes the composer and keeps the draft.
  • Cancel throws it away, asking first if anything has been filled in.
  • Publish adds the entry to your site and deploys it. It stays disabled until at least one field has something in it.

With more than one draft saved, the composer pages between them: swipe left or right on a touch screen, or use the arrows and dots below the fields.

What publishing writes

The commit message is written for you, as Add <shortcut name>: <title>. The title is the first field the draft has text in — the primary field of the list's first view when one is configured, and otherwise the first string or markdown field the type declares.

A new object is filed under that same text, lowercased and hyphenated, with a number appended if the name is already taken. A draft with no text anywhere is filed under the name of its type.

Publishing a draft publishes your site, so anything else you had left unsynced is deployed along with it.

Changing a schema under an open draft

A draft can outlive the shape it was started in. Every time one is opened it is reconciled against the current object definition: a field the type no longer declares is dropped, a field whose type has changed to one the saved value cannot fit is dropped with it, and a child list you have added since appears empty. Everything still valid is preserved, so editing your schema never costs you a draft outright.

ActivityPub

An activitypub section turns your site into an account people can follow from Mastodon, Threads, Ghost, and anything else that speaks ActivityPub. Every object you map becomes a post on that account, and each time your site deploys, the people following it see what you added, changed, or removed.

ActivityPub is part of Archival's hosting. Publishing runs when Archival deploys your site, so a site you build with the CLI and host elsewhere does not federate.

[activitypub]
username = "blog"
domain = "example.com"

[activitypub.actor]
object = "site"
name = "title"
summary = "bio"
icon = "logo"

[activitypub.objects.post]
when = "federate"
featured = "pinned"
name = "title"
content = "body"
published = "published_at"
image = "cover"

With this file, a site at example.com is followed as @blog@example.com.

Every value in the section other than username, domain, follow, type and the actor's object names a field of your object, exactly as it appears in archival_objects.toml — or, under attachment, one of its child lists and that list's fields. Nothing you write here is itself published: what goes out is whatever your content says.

activitypub is reserved at the top level of this file, so an object type named activitypub cannot be configured here.

The account

  • username (optional) — the part of the handle before the domain. It may hold letters, numbers and underscores, with dots and dashes allowed between them, up to 64 characters. Left out, the first part of the account's domain is used, with anything else turned into underscores, so my-site.com is followed as @my_site@my-site.com. A domain that starts with www gives you @www, so set a username if yours does.
  • domain (optional) — which of your site's domains the account lives on. It is the part of the handle after the username, and every post's address is written against it. Give a bare hostname such as example.com, with no https://, port or path. It must be one of your site's own domains. Left out, your site's primary domain is used. That is normally the domain the site was created with, not a custom domain you added later, so a site that started on an onarchival.dev address federates from that address unless you set this.
  • follow (optional) — accounts your site follows, up to 20, each a handle such as @user@example.com or the https address of an account. Each deploy follows every account newly on the list and unfollows every one taken off it. Your site does not show posts from the accounts it follows: following is how you opt in to services that ask for it, such as the Bluesky bridge. An account that cannot be found is tried again on the next deploy, and does not hold up publishing.

Settle on a domain before you start federating. If the account's domain changes afterwards — by setting or changing domain, or by renaming or removing your primary domain while domain is left out — it is published as a new account, and the followers you already have are not carried over.

[activitypub.actor] is optional, and sets the profile other servers show for the account. object is required, and names an object your site has exactly one of — a site settings or about object is the usual choice. The rest name its fields:

  • name — the display name. A string, markdown or enum field. Left out, the site_name in archival.toml is used, and failing that the domain.
  • summary — the bio. A string, markdown or enum field.
  • icon — the avatar. An image field.
  • image — the header image. An image field.

Bluesky

Bridgy Fed bridges fediverse accounts into Bluesky, and your site opts in by following its bot:

[activitypub]
follow = ["@bsky.brid.gy@bsky.brid.gy"]

Bridgy Fed only bridges an account with a profile picture, so map the actor's icon first. Once the bot follows your site back, new posts appear on Bluesky from @<username>.<domain>.ap.brid.gy, so @blog@example.com becomes @blog.example.com.ap.brid.gy. Posts older than two weeks are not bridged, apart from the newest featured post, which becomes the account's pinned post. Taking the bot off the list stops bridging.

Posts

The section must map at least one object type under activitypub.objects. Each table there is keyed by an object type your site has many of, and every object of that type is published unless when says otherwise:

  • when (optional) — a boolean field deciding which objects are published. Only objects where it is true are federated, and one whose field turns false is deleted from the servers that received it. Left out, every object of the type is published.

  • featured (optional) — a boolean field choosing the account's featured posts: the five newest posts where it is true, by published date, across every type you map. Mastodon, Pleroma, Akkoma and GoToSocial show them as pinned posts on the account's profile, and Bluesky, through Bridgy Fed, pins the newest one. Misskey, Sharkey and Iceshrimp pin only notes, so featured articles are not pinned there. Featuring or unfeaturing a post updates the account's profile, not the post. Left out, none of the type's posts are featured.

  • type (optional) — "Article" (the default) or "Note". An article is a piece of writing with a title. Mastodon never shows an article's body: it shows the title, the summary and a link to the post, with the post's image or, when it has none, a preview of the linked page. Servers such as Misskey show the whole article. A note is a short post like a status update, and is shown in full wherever it is read. A note has no title: its name is published only when it has no content, and then as its body, in plain text.

  • name — the title. A string, markdown or enum field.

  • content — the body. A string, markdown or enum field. Markdown is rendered to HTML.

  • summary — a string, markdown or enum field. Mastodon shows an article's summary as a teaser under its title, but Misskey, GoToSocial and many other servers show it as a content warning with the body hidden behind it, and on a note Mastodon and most other servers do the same. Only map it when you mean it as a warning. An article without one still gets a teaser on Mastodon from the preview of the page it links to, so set an og:description on that page instead.

  • published — when the post was published. A date field. Left out, or left empty on an object, the post is dated when it was first federated.

  • updated — when the post last changed. A date field. Left out, an edited post is dated by the deploy that changed it.

  • image — an image field, attached to the post. The image's description is used as its alt text.

  • attachment — the photos, videos and audio shown with the post, read from a child list. It is a table of its own:

    [activitypub.objects.post.attachment]
    children = "media"
    file = "file"
    name = "alt"
    

    children names the child list, and each of its entries is attached to the post in order, after image. file names an image, video or audio field of each entry, or a oneof with one of those among its options; an entry whose file is empty or another kind of upload is left out. name (optional) names a string, markdown or enum field of each entry, used as the attachment's alt text in place of the file's description. Mastodon shows only the first four attachments of a post.

Each type must map at least one of content or name. Fields you do not map are never published, and a secret field cannot be mapped at all. name and summary are sent as written rather than rendered from markdown, so keep them to plain text.

If your site keeps drafts — objects it holds but does not show — map when to the field that decides, or they are published along with everything else. An object that does not set the field at all counts as false. On a type that is not federating yet, adding a field and mapping it publishes nothing until you turn it on object by object. On a type that is already federating, every post where the field is not true is deleted from the servers that received it, so set it to true on the posts you want to keep in the same deploy that adds when.

When an object type has a page template, each post links to its page, and pasting that page's address into Mastodon's search finds the post.

When things are published

Publishing happens when your site deploys, however the change reached it — from the editor, a shortcut, or a git push. Nothing is sent while you are still editing.

  • Added objects are posted, changed objects are updated, and removed objects are deleted from the servers that received them. An object whose when field turns false is deleted the same way, and so is every object of a type you remove from activitypub.objects, as long as another type is still mapped.
  • An object only counts as changed when its post would come out different. Editing a field you have not mapped sends nothing, and changing this section re-renders every post and updates each one that comes out different. Renaming an object's file deletes its post and publishes a new one.
  • The first deploy with activitypub configured sends nothing to anyone. Your existing content is published to the account, but it will not arrive in anyone's timeline as though it were new.
  • Servers that discover the account do not pull in its older posts. They show the featured posts they can pin, and each post delivered after someone there follows it. Any other post with a page can still be found by pasting the page's address into their search.
  • A single deploy delivers at most 10 new posts and 20 updates, choosing the posts with the latest published dates, so importing a backlog does not flood your followers. Every other new post is still published, and one with a page can be found by the page's address. A server that already has a post keeps the version it last received. Deletions, and changes to the account's profile or to which posts are featured, are always delivered.

Any mistake in the section stops publishing: a setting it does not recognise, a username that is not allowed or a domain that is not one of your site's domains, a follow entry that is not an account, an object your site does not have (or a list where a single object is expected, or the reverse), or a field your object does not have or whose type cannot fill it (such as a when or featured field that is not a boolean). That deploy publishes nothing to the fediverse, and neither does any later one until you correct it. An account that is already federating keeps showing what it last published. One that has not federated yet, or that you are turning back on, stays unreachable. Your site itself still deploys as usual. Nothing in the editor reports these mistakes yet, so if new posts stop arriving, check the section against archival_objects.toml.

Followers, likes, boosts and replies

Archival keeps what the fediverse does with your account: who follows it, and the likes, boosts and public replies its posts get, including replies to those replies. Followers-only replies and direct messages are not kept. Edits, deletions and taken-back likes apply as they arrive, and an account that is deleted takes everything it did with it.

Your pages are built before any of that happens, so they cannot show it. A carrier can: site.activitypub reads each post's likes, boosts and replies when a visitor asks, so a post's page can fetch them and show the conversation under it. See Reading the fediverse.

Turning it off

Removing the activitypub section and deploying stops the account from answering. Its followers, posts, the accounts it follows and the likes, boosts and replies it has received are kept, and adding the section back resumes it where it left off. While it is off, a carrier reads the account as not federating, with nothing on any list. The same happens while a site's subscription is cancelled: the account stops answering, and the next deploy after it is reactivated brings it back.

Archival reserves the paths under /_archival/ap/ on every site it hosts, whether or not the site federates, so your site cannot serve its own files there. While a site federates, Archival also answers /.well-known/webfinger for its account and requests for ActivityPub documents on its behalf. It answers /.well-known/nodeinfo, /api/v1/instance and /api/v2/instance as well, describing the site as a single-account server. Any of those requests it does not recognise is passed on to your site.

Webmention

A webmention section lets your site take part in Webmention: when a page elsewhere links to one of yours, its site can tell yours, and when one of your objects links to a page elsewhere, your site tells theirs. Blogs on WordPress, Ghost, micro.blog and most of the IndieWeb speak it, and so does Bridgy, which turns likes and replies on Mastodon and Bluesky into mentions.

Webmention is part of Archival's hosting, like ActivityPub. Receiving and sending both run when Archival serves and deploys your site, so a site you build with the CLI and host elsewhere takes no part.

The smallest section receives without sending:

[webmention]

Everything the section accepts:

[webmention]
domain = "example.com"

[webmention.author]
object = "site"
name = "title"
photo = "logo"

[webmention.objects.post]
when = "live"
name = "title"
content = "body"
published = "published_at"
in_reply_to = "reply_to"
like_of = "liked"

Every value other than domain and the author's object names a field of your object, exactly as it appears in archival_objects.toml. Nothing you write here is itself sent: what goes out is whatever your content says.

webmention is reserved at the top level of this file, so an object type named webmention cannot be configured here.

Receiving

With the section present, every page of your site advertises an endpoint, and Archival takes the mentions sent to it. Each one is checked before it is kept: the page that claims to link to yours is fetched, and the mention counts only when it does link to a page your site serves. The page's microformats say what kind of mention it is — a reply, a like, a repost, a bookmark, or a plain mention — along with its author, its text and when it was written. A page that stops linking, or goes away, takes its mention with it the next time its site sends.

Your pages are built before any of that happens, so they cannot show it. A carrier can: site.webmentions reads each page's mentions when a visitor asks. See Reading mentions from other sites.

Nothing is moderated: any site can send a mention of any page, and what it says is kept as it says it. Each sending site is limited to 30 mentions a minute, a site keeps at most ten thousand, and a carrier can leave out whatever you would rather not show.

Sending

Each table under webmention.objects is keyed by an object type your site has many of, and one with a page template, since a mention names the page it comes from. Every object of the type sends a mention to each page it links to:

  • content — the body. A string, markdown or enum field. Every https link in it to a page off your site is sent a mention. Markdown is rendered to HTML.
  • in_reply_to — a string field holding the address of the page this object replies to. A reply is what a blog shows as a comment.
  • like_of — a string field holding the address of the page this object likes.
  • name — the title. A string, markdown or enum field.
  • published — when the object was published. A date field.
  • when (optional) — a boolean field deciding which objects send. Only objects where it is true send, and one whose field turns false takes its mentions back. Left out, every object of the type sends.

Each type must map at least one of content, in_reply_to or like_of, since those are what name the pages a mention reaches. A secret field cannot be mapped.

[webmention.author] is optional, and sets who the receiving site shows as the mention's author. object names an object your site has exactly one of; name is a string, markdown or enum field and photo an image field. Left out, the author is your site, named by its domain.

domain (optional) is which of your site's domains mentions are sent from. Give a bare hostname such as example.com, with no https://, port or path; it must be one of your site's own domains. Left out, your site's primary domain is used, which is normally the one the site was created with. Each mention is sent from a small page Archival serves on that domain holding your object's content as microformats. It is what the receiving site reads to verify the mention, so your own pages need no microformats of their own.

When mentions are sent

Sending happens when your site deploys, however the change reached it. Nothing is sent while you are still editing.

  • A new object sends a mention to every page it links to. A changed object sends one to every page it linked to before and after the change, so a page it no longer links to can drop the mention. A removed object, or one whose when field turns false, sends one to every page it linked to, and the page Archival served for it answers that it is gone, which is how the other site knows to remove the mention.
  • An object only counts as changed when what it says would come out different. Editing a field you have not mapped sends nothing, and neither does changing the author, moving to another domain, or anything else that only changes how its page is written.
  • New objects go out ten at a time, a minute apart, so importing a backlog or mapping a type for the first time does not reach every site it links to at once. Every object is sent eventually; changes and removals are never held back.
  • Each object sends to at most 50 pages.
  • A page that does not take mentions is skipped, and one whose site is down is tried again over the following hours.

Any mistake in the section stops sending: a setting it does not recognise, a domain that is not one of your site's domains, an object your site does not have (or a list where a single object is expected, or the reverse), a type with no page template, or a field your object does not have or whose type cannot fill it. That deploy sends nothing, and neither does any later one until you correct it; receiving carries on. Nothing in the editor reports these mistakes yet, so if mentions stop arriving on other sites, check the section against archival_objects.toml.

Turning it off

Removing the webmention section and deploying stops your site taking mentions. The mentions it has received are kept, and adding the section back resumes it. The same happens while a site's subscription is cancelled. Taking the objects tables out, with the section left in place, takes back every mention your site sent and keeps receiving.

The pages Archival served for your objects stay up either way, so a mention already on another site can still be verified. If your site loses a domain it sent mentions from, those mentions can no longer be updated or taken back.

Archival reserves the paths under /_archival/webmention on every site it hosts, whether or not the site uses it.