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 gives the people who work on the site accounts of their own there, ATProto, which makes your site an account on Bluesky's network, 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).

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.

Publishing your site any other way leaves your drafts 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.

ActivityPub

An activitypub list gives your site accounts people can follow from Mastodon, Threads, Ghost, and anything else that speaks ActivityPub. One account publishes your content: every object you map becomes a post on it, and each time your site deploys, the people following it see what you added, changed, or removed. The other accounts are the people who work on the site. Each is a handle of its own on your domain that its owner can follow, like, boost and reply from while reading anywhere on the fediverse.

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 = "jesse.news"

[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"

[[activitypub]]
username = "jesse"
email = "jesse@jesse.news"
follow = ["@bsky.brid.gy@bsky.brid.gy"]

With this file, a site at example.com is followed as @blog@example.com, and whoever was invited to the site as jesse@example.com is @jesse@example.com.

Each [[activitypub]] table is one account. Every value in it other than username, domain, email, 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. 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. So is allow_mastodon_clients, described under Mastodon apps.

Accounts

  • username (required when there is more than one account) — 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, and no two accounts may share one. With a single account it may be left out, and 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.
  • email (optional) — whose account this is: the address a collaborator was invited to the site with. Only that person can act from the account. Left out, anyone who can edit the site can. The account's name and avatar come from actor when it is set, and otherwise from that person's Archival profile, or the site's name.
  • follow (optional) — accounts this account follows, up to ten thousand, 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, and how a person's account is seen by the people they read. An account that cannot be found is tried again on the next deploy, and does not hold up publishing. Pressing Follow from the editor, described under Acting from your account, edits this list for you.

Settle on a domain before you start federating. If an 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. Renaming an account's username does the same.

[activitypub.actor] is optional on any account, and sets the profile other servers show for it. object is required, and names an object your site has exactly one of — a site settings or about object for the account that publishes, or an author's entry for a person's. 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

A site reaches Bluesky on its own with an atproto section, which makes the site an account there at @example.com. That is the way to be on Bluesky.

Bridgy Fed is the other way: it bridges a fediverse account into Bluesky under a handle of the bridge's. Use one or the other, since each is a separate Bluesky account with its own followers. An account opts in to the bridge 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 the account 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

One account must map at least one object type under its objects, and an object type may be mapped by only one account, which is the account its posts come from. 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 the account maps. 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.

  • 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 take out of an account's objects, as long as some account still maps a type.
  • 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, and so does moving a type from one account to another.
  • The first deploy of an account 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 an 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 per account, 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 an 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 that two accounts share, a domain that is not one of your site's domains, an email that is not a collaborator's, a follow entry that is not an account, an object type mapped by two accounts, 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.

Acting from your account

A person's account is used from the Archival editor, signed in as yourself. Whoever can sign in to the editor and matches the account's email is the account.

The way in is the one every Mastodon-compatible server offers. Reading on any of them while logged out, press Follow on a profile, or Star, Boost or Reply on a post. The server asks which account you want to act from; give your handle, jesse@example.com, and it sends you to the editor, which shows the profile or post and a button for the action. Most servers remember the handle, so after the first time it is a single click. The same page in the editor takes a pasted handle or address, so you can act on anything without going through another server first.

  • Follow and Unfollow edit the account's follow list in this file and commit the change, so the follow is sent when your site next deploys. Following in the editor and editing the list by hand are the same thing.
  • Like, Boost and Reply, and taking any of them back, are sent at once from your account. A reply is published as a post of the account, kept by Archival and shown under the original wherever its author's server shows replies. When the page you are replying to or liking also takes Webmention, the same reply or like is sent as a mention.

An account without email can be used by anyone who can edit the site, so the account that publishes your posts can reply from the editor too. The editor shows no timeline: to read what an account follows, use a Mastodon app, described next.

Mastodon apps

allow_mastodon_clients = true

[[activitypub]]
username = "jesse"
email = "jesse@example.com"
follow = ["@someone@mastodon.social"]

With allow_mastodon_clients = true at the top of this file, any app made for Mastodon (Ivory, Tusky, Elk, Phanpy, and most others) can sign in to one of your site's accounts. In the app, add a server and give your site's domain, example.com. The app opens the Archival editor in a browser, where you sign in as yourself and choose which of the site's accounts the app may act as: your own, when its email is yours, or any account without one. Approve, and the app is signed in with a key of its own. Signing out in the app gives the key back, and so does the editor: the page you act from your account on, described above, lists the apps signed in to the site's accounts with a Sign out button for each.

What the app shows is the account's home timeline: every post and boost from the accounts on its follow list, kept by Archival as they arrive, so it starts empty and fills from the moment the follows are accepted. The last two thousand are kept. Posts addressed to followers only are shown; direct messages are not. From the app you can star, boost and reply to anything on that timeline, delete a reply you wrote, search for an account or a post by handle or address, and follow or unfollow an account — the same actions as the editor's, and they work the same way: a reply is published as a post of the account, and a follow edits this file and is sent when the site next deploys, so the app shows it as a pending request until then. The app cannot write a post of its own, because an account's posts are the site's objects: write them in the site. Notifications, lists, filters and bookmarks are empty, and the app's other screens show nothing, since the site keeps none of that.

Turning on apps reserves three more paths on every domain of the site, /oauth/, /api/v1/ and /api/v2/, because that is where Mastodon apps look. A page or file of yours at one of those paths is still served when the app's request is one Archival does not answer, but a path there cannot be relied on. Set the line to false or remove it, and deploy, to turn apps off; every app that was signed in stops working at once.

Followers, likes, boosts and replies

Archival keeps what the fediverse does with each account: who follows it, and the likes, boosts and public replies its posts get, including replies to those replies. 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

Taking an account out of the list and deploying stops it answering; removing the whole activitypub list stops every account. Their followers, posts, the accounts they follow and the likes, boosts and replies they have received are kept, and adding an account back with the same username and domain 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 accounts stop answering, and the next deploy after it is reactivated brings them 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 accounts and requests for ActivityPub documents on their behalf. It answers /.well-known/nodeinfo, /api/v1/instance and /api/v2/instance as well, describing the site as a server with as many users as it has accounts and registrations closed. Any of those requests it does not recognise is passed on to your site. What exactly is served, for people writing servers and tools, is on the Federation page.

ATProto

An atproto section makes your site an account on the AT Protocol, the network Bluesky runs on. Its handle is your domain, and every deploy publishes the objects you map as Bluesky posts or long-form documents:

[atproto]

[atproto.objects.note]
content = "text"
published = "date"

It is configured the way activitypub is, by naming fields, and a site can have both. ATProto & Bluesky covers every key, what is published, and the one line a template needs for long-form documents to be verified.

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"
bookmark_of = "link.url"

Every value other than domain and the author's object names a field of your object, exactly as it appears in archival_objects.toml. 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:

  • bookmark_of — the address of the page this object bookmarks or shares, read the same way as in_reply_to. A post that shares a link sends its mention as a bookmark.
  • 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.

bookmark_of, in_reply_to and like_of can also read the address from inside a meta field, or a custom editor type whose type is meta: write the field, a dot, and the key that holds the address, such as link.url for a link field holding url = "https://..." among its other keys. Keys can go deeper, as in link.image.url. Archival can only check that link is a meta field, so a key that is missing, or holds anything but an https address, sends nothing for that object; the same goes for a string field that is not an https address.

Each type must map at least one of content, in_reply_to, like_of or bookmark_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. Instead of an object, account names the username of one of your ActivityPub accounts, and the author is that account's profile. 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.

  • 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.