洪 民憙 (Hong Minhee) :nonbinary:'s avatar

洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social

1,097 following1,871 followers

An intersectionalist, feminist, and socialist living in Seoul (UTC+09:00). @tokolovesme's spouse. Who's behind @fedify, @hollo, and @botkit. Write some free software in , , , & . They/them.

서울에 사는 交叉女性主義者이자 社會主義者. 金剛兔(@tokolovesme)의 配偶者. @fedify, @hollo, @botkit 메인테이너. , , , 等으로 自由 소프트웨어 만듦.

()

@hongminhee@hollo.social · Reply to fedicat

@fedicat Yeah, the quote post collision was a clear example. Whether it's deliberate or just indifference, the Mastodon team doesn't seem to treat the API as a shared surface that evolves across implementations. Hard to build on top of that.

@hongminhee@hollo.social · Reply to fedicat

@fedicat That's fair. I know other platforms extend the Mastodon API, but I've been reluctant to do the same. It doesn't feel like a surface that's meant to be extended, and I'd rather not pile more non-standard behavior on top of an API that's already a moving target.

@hongminhee@hollo.social · Reply to Dawn Ahukanna

@dahukanna I'm not sure I'm following the storage question. Could you say more about what you mean by runtime data here?

On the second point: the Mastodon API and ActivityPub are completely different in design. They're separate specifications: the Mastodon API is a REST API, ActivityPub is an entirely different protocol. The two look nothing alike, so I'm not sure what “Mastodon instance API that implements ActivityPub” refers to.

@hongminhee@hollo.social

The more I work on Hollo, the more its Mastodon-compatible API feels like a straitjacket. I chose it for a practical reason: people could use existing clients, and I wouldn't have to build a frontend too. I still think that was the right call at the time.

The problem is that clients can only expose what Mastodon's API represents. I keep coming up with ideas, then asking whether the API has anywhere to put them. If it doesn't, I have no way to get the feature in front of users.

ActivityPub API (so-called “C2S”) might be the eventual answer, but it doesn't help much today. I don't know of any clients Hollo users could realistically use with it, and adoption is still limited.

So I keep coming back to the idea that Hollo will need a frontend of its own. I'd keep the Mastodon API for existing apps. I'm not especially eager to take on a whole frontend, but I don't see another way to try ideas that don't fit Mastodon's model.

@nuages@mediaformat.org

Hello World

Coucou! This is just a first post announcing Nuages to the world.

Nuages is an ActivityPub app, connecting to your favourite servers that support the Client to Server API.

Follow us to get our latest updates, we’ll be officially launching the app in the coming weeks 😉

mediaformat.org

Nuages – MediaFormat

Do you yearn for a user powered social network? A social space free from constant Ads, Sponsored and AI generated slop? The Fediverse 1 is a network where you c

@django@social.coop

Announcing it here publicly for the first time!

I've been building a general purpose activitypub pwa.

Follow @nuages for official updates!

mediaformat.org

Hello World – MediaFormat

Coucou! This is just a first post announcing Nuages to the world. Nuages is an ActivityPub app, connecting to your favourite servers that support the Client to

@nuages@mediaformat.org

Hello World

Coucou! This is just a first post announcing Nuages to the world.

Nuages is an ActivityPub app, connecting to your favourite servers that support the Client to Server API.

Follow us to get our latest updates, we’ll be officially launching the app in the coming weeks 😉

mediaformat.org

Nuages – MediaFormat

Do you yearn for a user powered social network? A social space free from constant Ads, Sponsored and AI generated slop? The Fediverse 1 is a network where you c

@hongminhee@hollo.social

I recently moved my personal blog from a PHP setup to Astro on Netlify because I wanted to add ActivityPub support with Fedify.

Somehow, it took me until then to realize that I had never actually tried running Fedify on Netlify.

Netlify Database is PostgreSQL, so the persistence side should already fit @fedify/postgres. The less obvious part is background delivery: Netlify Functions cannot keep a queue consumer alive, while Async Workloads uses a push-based model similar to WorkersMessageQueue in @fedify/cfworkers.

So I’m going to try making @fedify/netlify, with my own blog as its first real test case. I wrote down the initial design in issue #930.

github.com

`@fedify/netlify` for Netlify Async Workloads · Issue #930 · fedify-dev/fedify

Background Fedify can serve federation requests from Netlify Functions through the standard Request and Response APIs. Persistent state also does not need a Netlify-specific adapter: Netlify Databa...

@COSCUP@floss.social

🎉 COSCUP x UbuCon Asia 2026 Welcome Party is ON!

Our favorite pre-con warmup is back ✨ No presentation pressure, just a super chill gathering.

First-timer or old friend, come hang out, catch up, and share what cool projects you’ve been hacking on lately! 💬

📅 Fri, Aug 7 | 18:15–21:30
📍 Cosmos Bistro (Hua Shan Din)
🎟️ Pass: s.coscup.org/welcomeparty

💡 Free to drop in, no pre-registration required! Grab a pass to support the community and make it an amazing night ✨

coscup-tw.kktix.cc

COSCUP x UbuCon Asia 2026 Welcome-Party 前夜派對

前夜派對(Welcome Party)是 COSCUP 活動前一晚的暖身聚會。不論你是否參與隔天的 COSCUP,都歡迎來和大家喝一杯、聊開源,先認識社群夥伴,也讓接下來兩天的大會更容易開始交流。

@hongminhee@hollo.social · Reply to 🫧 Social coding commons

@smallcircles Fair, and I'll be honest, I haven't read the linked post yet, it's a long one and I want to give it a proper read rather than skim it.

I'd like to be more involved in the standardization side of this too. Language is a real hurdle for me there, participating in that kind of discussion in a second language is harder than it looks, so bear with me if I'm slower to engage than I'd like. That said, writing this case up as a FEP seems worth trying, and I might take a shot at it.

@hongminhee@hollo.social

's inbox treats object types in tiers. Note and Question are supported directly. A second tier, Article, Page, Image, Audio, Video, and Event, gets converted to a title, a summary, and a link; content itself doesn't show. Anything outside both tiers is dropped on delivery, which is a large part of why so much of the fediverse federates as Note regardless of what the object actually is.

mastodon/mastodon#24079 asks for content to show up whenever it's present and the mediaType is supported, independent of type. Open since 2023, and as far as the thread shows, no core team engagement beyond one offhand remark that they're not happy with the status quo either.

Reading the issue text closely, it's a bit ambiguous whether it covers the drop tier or only the convert-to-title-and-link tier. I left a comment asking for clarification, since the fix looks very different depending on the answer.

If you build on ActivityPub, a comment or a reaction there is worth more than another workaround.

github.com

Always show object `content`, regardless of object `type`, if `mediaType` is supported · Issue #24079 · mastodon/mastodon

Pitch Currently, Mastodon separates ActivityPub objects between “supported” and “converted”, based on object type. Converted objects will only display a title, spoiler text and URL. This is the cas...

@botkit@hackers.pub · Reply to BotKit by Fedify

ZennにBotKitを使ってフェディバースのRSSボットを作成するチュートリアルが公開されました。DenoとNode.jsの両方に対応しており、動的にRSSフィードを追加するところまで実装しています。ActivityPubの開発を全く知らない方でも簡単に進められるように書きましたので、ぜひ一度ご覧ください!

zenn.dev

RSSフィードを監視してフェディバースに投稿するボットをBotKitで作る

@botkit@hackers.pub

A tutorial on building a fediverse RSS bot is now up on the BotKit website. It supports both Deno and Node.js, and even covers how to dynamically add RSS feeds. It's designed to be easy to follow even if you have zero experience with ActivityPub development, so be sure to check it out!

canary.botkit.fedify.dev

Building an RSS bot | BotKit by Fedify

Build an RSS/Atom feed bot with BotKit, test it on the fediverse, then grow it into a multi-bot instance and deploy it.

@julian@fietkau.social

Having some nice ActivityPub design/implementation conversations with @hongminhee in the @fedify Matrix chat at :matrix.org

And if it sometimes boils down to an exchange of 😮‍💨 and 😞, then maybe that's what we need that day, and 💪 can be for tomorrow.

@kodingwarrior@silicon.moe

Mastodon 4.6 has just been released, introducing a new "Collections" feature. Like Bluesky's Starter Packs, this lets us curate and recommend accounts to newcomers on the fediverse, so they don't land on an empty, lifeless timeline when they first join.

@kodingwarrior@hackers.pub

Sometimes, while designing some hypothetical fediverse software, I run into this feeling

setting aside ActivityPub spec compatibility itself, there are these frustrating moments where I hit preconditions like 'this won't work because of which FEPs (e.g. Activity Intents) other fediverse software happens to support' and it just kills the momentum.

@hongminhee@hollo.social

One thing I keep coming back to when comparing and AT Protocol: ActivityPub is basically a convention on top of the stuff we already have. An actor is a JSON-LD document you GET. An inbox is an endpoint you POST to. If you already run a website, you can bolt this on without changing the site's basic shape. Ghost didn't set out to join the , and then years later it could, just by adding an endpoint.

AT Protocol feels less like that to me. Running a PDS means a signed repo, a Merkle search tree, a firehose, a DID. It's not a layer you add to an existing site. It's closer to standing up another backend beside it.

I think that's why ActivityPub keeps making sense to me. You can join later. You don't have to have built the whole thing with federation in mind from day one.

@hongminhee@hollo.social

Requested a ID through a Security Advisory on June 27 and still nothing, almost two weeks now. GitHub's docs say it usually takes up to three days.

Anyone else seeing longer waits from the GitHub CNA lately, or is this just me? Curious whether it's a general slowdown or something specific to my request.

@hongminhee@hollo.social · Reply to Beady Belle Fanchannel

@Profpatsch Oh, the quote seems still pending. I'm quoting it: https://tldr.nettime.org/@tante/116880003584050912.

tldr.nettime.org

tante (@tante@tldr.nettime.org)

If you are a member of Codeberg e.V. please take the time to participate in the poll that was just sent out about banning vibe-coded projects on Codeberg. Please agree to the proposal. Slop can live on GitHub.

@tante@tldr.nettime.org

If you are a member of Codeberg e.V. please take the time to participate in the poll that was just sent out about banning vibe-coded projects on Codeberg.

Please agree to the proposal. Slop can live on GitHub.

@hongminhee@hollo.social

Just dropped a new BotKit release, and BotKit now can host multiple bots per instance! Also we have a new beautiful website as well: botkit.fedify.dev.

botkit.fedify.dev

Standalone ActivityPub bots in TypeScript

BotKit is a TypeScript framework for building standalone ActivityPub bots for the fediverse: its own server, not a Mastodon or Misskey account. Built by the Fedify team.

@botkit@hackers.pub

We're pleased to announce BotKit 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 FEP-044f, 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.

A new look for botkit.fedify.dev

Until this release, botkit.fedify.dev read like a stock VitePress site: a workable but generic shell wrapped around the docs, with none of BotKit's own personality showing through.

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.

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.

The deployment guides grew alongside the new landing page: they were split apart and fleshed out, and a new Cloudflare Workers guide joins the existing Deno Deploy, Docker, and self-hosting guides.

A new look for your bot's pages

The pages BotKit serves for your 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.

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 PagesOptions.theme option ("auto", "light", or "dark") 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.

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.

If you want to go further than the accent color allows, PagesOptions.css still lets you inject custom CSS on top of BotKit's own stylesheet.

Multi-bot instances

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 #16, is gone: the new createInstance() function creates an Instance 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.

For a fixed, known set of bots, Instance.createBot() takes an identifier and a profile:

import { createInstance, text } from "@fedify/botkit";
import { MemoryKvStore } from "@fedify/fedify";

const instance = createInstance<void>({ kv: new MemoryKvStore() });

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

greetBot.onFollow = async (session, followRequest) => {
  await followRequest.accept();
  await session.publish(text`Welcome, ${followRequest.follower}!`);
};

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:

const weatherBots = instance.createBot(async (ctx, identifier) => {
  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) => {
  const code = session.bot.identifier.slice("weather_".length);
  await message.reply(text`Current weather: ${await db.getWeather(code)}`);
};

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 /@{username}; a reserved instance actor signs shared-inbox requests when there's no single bot whose key obviously should.

None of this touches single-bot deployments: createBot() 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 Repository implementation, though, this is a breaking change worth planning for: every method now takes the owning bot's identifier as its first parameter, Session.bot is a read-only ReadonlyBot instead of a mutable Bot, 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 Instance concept document.

Thanks to @moreal, 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.

BotKit has supported quoting since 0.2.0, but only in the Misskey family's style: a quoteUrl property and a Link tag, sent without ever asking the quoted author. Mastodon 4.4 and 4.5 do things differently: they verify quotes through FEP-044f'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 of a BotKit bot showed up as unverifiable.

BotKit now handles the FEP-044f handshake in both directions. When you quote a message, it sets the FEP-044f quote property and sends a QuoteRequest to the quoted author, alongside the Create it already sent. Publishing stays non-blocking: the post goes out immediately, and the quote is upgraded once (or if) approval arrives:

const message = await session.publish(text`This message quotes another one.`, {
  quoteTarget: quoted,
});
console.log(message.quoteApprovalState); // "pending"

On the receiving side, a new quotePolicy option (on createBot(), and per message on Session.publish()) controls how your bot answers incoming quote requests: automatically for everyone ("public", the default), automatically for followers only, never ("nobody"), or held for manual review through the new Bot.onQuoteRequest event:

bot.onQuoteRequest = async (session, request) => {
  if (request.state === "pending") await request.accept();
};

Bot.onQuoteAccepted, Bot.onQuoteRejected, and Bot.onQuoteRevoked cover what happens next for quotes your own bot sent, Message.quoteApproved reports whether an incoming quote carries a valid authorization stamp, and AuthorizedMessage.unauthorizeQuote() lets you revoke one you previously granted. The legacy quoteUrl 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 #27 through #33.

A Redis repository (@fedify/botkit-redis)

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 @fedify/botkit-redis package fills that gap with RedisRepository, built directly on Redis strings, sets, and sorted sets rather than going through a generic key–value abstraction:

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" }),
});

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 node-redis client you inject and keep control of, and it's available for both Deno and Node.js. #12 and #35 cover the rest of it.

Smaller improvements

The npm package's TypeScript declaration files no longer accidentally include the runtime Temporal polyfill code, which had been leaking into consumers' .d.ts output. Fedify was upgraded to 2.3.1, Hono to 4.12.27, and LogTape to 2.2.3.


As always, the full list of changes is in CHANGES.md, and every API mentioned above is documented at botkit.fedify.dev. Thank you to everyone who filed issues, opened discussions, and tried BotKit out.

If you build something with BotKit, run into a rough edge, or just want to talk through an idea before opening an issue, GitHub Discussions is the place for exactly that. For something closer to real time, BotKit's chat now lives on Matrix at #fedify:matrix.org. Drop in and say hello.

matrix.to

You're invited to talk on Matrix

You're invited to talk on Matrix