<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[BotKit 0.5.0: A new design language, multi-bot instances, and consent-respecting quotes]]></title><description><![CDATA[<p dir="auto">We're pleased to announce <a href="https://botkit.fedify.dev/" target="_blank" rel="noopener noreferrer">BotKit</a> 0.5.0. This release changes how both the docs site and every bot's own pages look, and it stops limiting a server to one bot: a single process can now host a whole fleet of them. Quoting, and being quoted, goes through a consent step under <a href="https://w3id.org/fep/044f" target="_blank" rel="noopener noreferrer">FEP-044f</a>, and a Redis-backed repository joins the SQLite and PostgreSQL ones for shared production storage. A few of these changes are breaking; see below for what to check before you upgrade.</p>
<h2><a class="anchor-offset" name="a-new-look-for-botkit.fedify.dev"></a>A new look for botkit.fedify.dev</h2>
<p dir="auto">Until this release, <a href="https://botkit.fedify.dev/" target="_blank" rel="noopener noreferrer">botkit.fedify.dev</a> read like a stock VitePress site: a workable but generic shell wrapped around the docs, with none of BotKit's own personality showing through.</p>
<p dir="auto">The theme and the landing page are both new. The palette now matches the real logo greens instead of a generic default, and headlines are set in Space Grotesk, paired with Inter for everything else; the display font is self-hosted, so no visitor's IP is handed to a font CDN. The new landing page frames BotKit's dinosaur mascot inside its signature unassembled-model-kit sprue frame, then leads with a tabbed installer for Deno, npm, pnpm, and Yarn and a one-file bot example before getting into what BotKit actually does: messages, events, multi-bot instances, and the pages it builds for a bot without any extra work from you.</p>
<p dir="auto"><img src="https://media.hackers.pub/media/15aa3e0fb9912dd140ff5aa180beddf035d8c34e4eaa04c60fd3502bd3d938f8.webp" alt="The redesigned botkit.fedify.dev homepage: a framed dinosaur mascot in a model-kit sprue, a one-file install snippet, and a rotating code sample." class=" img-fluid img-markdown" /></p>
<p dir="auto">The deployment guides grew alongside the new landing page: they were split apart and fleshed out, and a new <a href="https://botkit.fedify.dev/deploy/cfworkers" target="_blank" rel="noopener noreferrer">Cloudflare Workers guide</a> joins the existing Deno Deploy, Docker, and self-hosting guides.</p>
<h2><a class="anchor-offset" name="a-new-look-for-your-bot-s-pages"></a>A new look for your bot's pages</h2>
<p dir="auto">The pages BotKit serves for <em>your</em> bot (its profile, its posts, its follower list) got the same attention, in the opposite direction. Until now they were built on Pico CSS pulled from an external CDN: a fine default, but a generic one that made every BotKit-hosted bot look like a demo of the same template, and that quietly sent every visitor's browser off to fetch a stylesheet from someone else's server.</p>
<p dir="auto">That's gone. Bot pages now use a self-contained design system, bundled with the package and served from BotKit's own content-addressed, cache-forever path: no external CDN, no build step, on either Deno or Node.js. The whole look is driven by a single accent color you choose (twenty names, the same legend Pico CSS uses, so your existing choice of color still works), tinting links, the follow button, and small highlights, while everything else stays quiet so your bot's name, avatar, and posts are what a visitor actually notices. A new <code>PagesOptions.theme</code> option (<code>"auto"</code>, <code>"light"</code>, or <code>"dark"</code>) controls the color scheme independently of the accent. A repost is now marked and attributed to its original author instead of blending into the bot's own timeline.</p>
<p dir="auto"><img src="https://media.hackers.pub/media/d6a84e47a619cff04164ebec091f67d4ff20cc1e83c686da742e8b399ed22981.webp" alt="A bot's redesigned profile page: a banner and avatar, bio with a link and mention, custom properties, follower and post counts, and a follow button, all tinted in the bot's chosen accent color." class=" img-fluid img-markdown" /></p>
<p dir="auto">If you want to go further than the accent color allows, <code>PagesOptions.css</code> still lets you inject custom CSS on top of BotKit's own stylesheet.</p>
<h2><a class="anchor-offset" name="multi-bot-instances"></a>Multi-bot instances</h2>
<p dir="auto">Until this release, a BotKit process could only ever be one bot. Running a second bot meant standing up a whole second server, even when the two bots could easily have shared the same infrastructure. That limitation, raised in <a href="https://github.com/fedify-dev/botkit/issues/16" target="_blank" rel="noopener noreferrer">#16</a>, is gone: the new <code>createInstance()</code> function creates an <code>Instance</code> that owns the shared plumbing (the key–value store, the message queue, the repository, and HTTP handling), and any number of bots can live on it side by side, each with its own actor identity and event handlers.</p>
<p dir="auto">For a fixed, known set of bots, <code>Instance.createBot()</code> takes an identifier and a profile:</p>
<pre><code class="language-typescript">import { createInstance, text } from "@fedify/botkit";
import { MemoryKvStore } from "@fedify/fedify";

const instance = createInstance&lt;void&gt;({ kv: new MemoryKvStore() });

const greetBot = instance.createBot("greet", {
  username: "greetbot",
  name: "Greeting Bot",
});

greetBot.onFollow = async (session, followRequest) =&gt; {
  await followRequest.accept();
  await session.publish(text`Welcome, ${followRequest.follower}!`);
};
</code></pre>
<p dir="auto">For a family of bots resolved on demand (one per region, one per customer, thousands of them backed by a database), pass a dispatcher function instead of a fixed identifier, and BotKit resolves and federates each one lazily:</p>
<pre><code class="language-typescript">const weatherBots = instance.createBot(async (ctx, identifier) =&gt; {
  if (!identifier.startsWith("weather_")) return null;
  const region = await db.getRegion(identifier.slice("weather_".length));
  if (region == null) return null;
  return { username: identifier, name: `${region.name} Weather Bot` };
});

weatherBots.onMention = async (session, message) =&gt; {
  const code = session.bot.identifier.slice("weather_".length);
  await message.reply(text`Current weather: ${await db.getWeather(code)}`);
};
</code></pre>
<p dir="auto">Incoming activities are routed only to the bots they actually concern: the followed bot, the owner of a liked or replied-to message, mentioned bots, and bots followed by the sender. Multi-bot instances also serve a list of hosted bots at the web root, with each bot's own pages moving to <code>/@{username}</code>; a reserved instance actor signs shared-inbox requests when there's no single bot whose key obviously should.</p>
<p dir="auto">None of this touches single-bot deployments: <code>createBot()</code> keeps working exactly as it always has, with the bot's pages staying at the web root and its data migrated to the new storage layout automatically on startup. If you maintain a custom <code>Repository</code> implementation, though, this is a breaking change worth planning for: every method now takes the owning bot's identifier as its first parameter, <code>Session.bot</code> is a read-only <code>ReadonlyBot</code> instead of a mutable <code>Bot</code>, and local object URIs carry the owning bot's identifier (old-format URIs are still recognized and permanently redirected, so links other servers stored keep working). The full picture, including how to move an existing single-bot deployment onto a multi-bot instance later, is in the new <a href="https://botkit.fedify.dev/concepts/instance" target="_blank" rel="noopener noreferrer"><em>Instance</em> concept document</a>.</p>
<p dir="auto">Thanks to <a class="plugin-mentions-user plugin-mentions-a" href="/user/moreal%40hackers.pub" aria-label="Profile: moreal@hackers.pub">@<bdi>moreal@hackers.pub</bdi></a>, whose early work-in-progress explorations of this problem surfaced its two hardest design questions (mapping usernames to identifiers for dynamic bots, and routing object URIs that used to carry no owner information) well before this implementation settled on its final shape.</p>
<h2><a class="anchor-offset" name="consent-respecting-quotes-with-fep-044f"></a>Consent-respecting quotes with FEP-044f</h2>
<p dir="auto">BotKit has supported quoting since 0.2.0, but only in the Misskey family's style: a <code>quoteUrl</code> property and a <code>Link</code> tag, sent without ever asking the quoted author. Mastodon 4.4 and 4.5 do things differently: they verify quotes through <a href="https://w3id.org/fep/044f" target="_blank" rel="noopener noreferrer">FEP-044f</a>'s consent handshake before showing them as quotes at all. Without that handshake, a BotKit bot's quotes never rendered as quotes on Mastodon, and quotes <em>of</em> a BotKit bot showed up as unverifiable.</p>
<p dir="auto">BotKit now handles the FEP-044f handshake in both directions. When you quote a message, it sets the FEP-044f <code>quote</code> property and sends a <code>QuoteRequest</code> to the quoted author, alongside the <code>Create</code> it already sent. Publishing stays non-blocking: the post goes out immediately, and the quote is upgraded once (or if) approval arrives:</p>
<pre><code class="language-typescript">const message = await session.publish(text`This message quotes another one.`, {
  quoteTarget: quoted,
});
console.log(message.quoteApprovalState); // "pending"
</code></pre>
<p dir="auto">On the receiving side, a new <code>quotePolicy</code> option (on <code>createBot()</code>, and per message on <code>Session.publish()</code>) controls how your bot answers incoming quote requests: automatically for everyone (<code>"public"</code>, the default), automatically for followers only, never (<code>"nobody"</code>), or held for manual review through the new <code>Bot.onQuoteRequest</code> event:</p>
<pre><code class="language-typescript">bot.onQuoteRequest = async (session, request) =&gt; {
  if (request.state === "pending") await request.accept();
};
</code></pre>
<p dir="auto"><code>Bot.onQuoteAccepted</code>, <code>Bot.onQuoteRejected</code>, and <code>Bot.onQuoteRevoked</code> cover what happens next for quotes your own bot sent, <code>Message.quoteApproved</code> reports whether an incoming quote carries a valid authorization stamp, and <code>AuthorizedMessage.unauthorizeQuote()</code> lets you revoke one you previously granted. The legacy <code>quoteUrl</code> and Misskey-style tags are still sent alongside the new property, so nothing about quoting on Misskey and its relatives changes. The full design is spread across <a href="https://github.com/fedify-dev/botkit/issues/27" target="_blank" rel="noopener noreferrer">#27</a> through <a href="https://github.com/fedify-dev/botkit/pull/33" target="_blank" rel="noopener noreferrer">#33</a>.</p>
<h2><a class="anchor-offset" name="a-redis-repository-code-@fedify-botkit-redis-code"></a>A Redis repository (<code>@fedify/botkit-redis</code>)</h2>
<p dir="auto">BotKit has had a SQLite repository since 0.3.0 and a PostgreSQL one since 0.4.0, but neither is the natural fit for a bot that runs as several worker processes sharing one store, which is exactly the shape a Redis-backed deployment usually takes. The new <code>@fedify/botkit-redis</code> package fills that gap with <code>RedisRepository</code>, built directly on Redis strings, sets, and sorted sets rather than going through a generic key–value abstraction:</p>
<pre><code class="language-typescript">import { createBot, MemoryKvStore } from "@fedify/botkit";
import { RedisRepository } from "@fedify/botkit-redis";

const bot = createBot({
  username: "mybot",
  kv: new MemoryKvStore(),
  repository: new RedisRepository({ url: "redis://localhost:6379/0" }),
});
</code></pre>
<p dir="auto">Because several workers can share one Redis instance, the read-modify-write paths that matter most under concurrency (message updates, follower bookkeeping, quote authorization indexes) are protected by short-lived locks that get renewed while a slow update is still running, rather than by assuming only one process ever touches the data at a time. The package supports both a connection URL it manages itself and an existing <a href="https://github.com/redis/node-redis" target="_blank" rel="noopener noreferrer">node-redis</a> client you inject and keep control of, and it's available for both Deno and Node.js. <a href="https://github.com/fedify-dev/botkit/issues/12" target="_blank" rel="noopener noreferrer">#12</a> and <a href="https://github.com/fedify-dev/botkit/pull/35" target="_blank" rel="noopener noreferrer">#35</a> cover the rest of it.</p>
<h2><a class="anchor-offset" name="smaller-improvements"></a>Smaller improvements</h2>
<p dir="auto">The npm package's TypeScript declaration files no longer accidentally include the runtime Temporal polyfill code, which had been leaking into consumers' <em>.d.ts</em> output. Fedify was upgraded to 2.3.1, Hono to 4.12.27, and LogTape to 2.2.3.</p>
<hr />
<p dir="auto">As always, the full list of changes is in <a href="https://github.com/fedify-dev/botkit/blob/0.5.0/CHANGES.md" target="_blank" rel="noopener noreferrer"><em>CHANGES.md</em></a>, and every API mentioned above is documented at <a href="https://botkit.fedify.dev/" target="_blank" rel="noopener noreferrer">botkit.fedify.dev</a>. Thank you to everyone who filed issues, opened discussions, and tried BotKit out.</p>
<p dir="auto">If you build something with BotKit, run into a rough edge, or just want to talk through an idea before opening an issue, <a href="https://github.com/fedify-dev/botkit/discussions" target="_blank" rel="noopener noreferrer">GitHub Discussions</a> is the place for exactly that. For something closer to real time, BotKit's chat now lives on Matrix at <a href="https://matrix.to/#/#fedify:matrix.org" target="_blank" rel="noopener noreferrer">#fedify:matrix.org</a>. Drop in and say hello.</p>
]]></description><link>https://forum.pierobosio.it/topic/6296d255-53ff-4655-9f11-fb54332565af/botkit-0.5.0-a-new-design-language-multi-bot-instances-and-consent-respecting-quotes</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Jul 2026 18:04:20 GMT</lastBuildDate><atom:link href="https://forum.pierobosio.it/topic/6296d255-53ff-4655-9f11-fb54332565af.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 08 Jul 2026 15:17:33 GMT</pubDate><ttl>60</ttl></channel></rss>