洪 民憙 (Hong Minhee)'s avatar

洪 民憙 (Hong Minhee)

@hongminhee@hollo.social · 956 following · 1270 followers

An intersectionalist, feminist, and socialist guy 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 메인테이너. , , , 等으로 自由 소프트웨어 만듦.

()

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

@hongminhee@hollo.social

Hello, I'm an open source software engineer in my late 30s living in , , and an avid advocate of and the .

I'm the creator of @fedify, an server framework in , @hollo, an ActivityPub-enabled microblogging software for single users, and @botkit, a simple ActivityPub bot framework.

I'm also very interested in East Asian languages (so-called ) and . Feel free to talk to me in , (), or (), or even in Literary Chinese (, )!

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee)'s post

安寧(안녕)하세요, 저는 서울에 살고 있는 30() 後半(후반) 오픈 소스 소프트웨어 엔지니어이며, 自由(자유)·오픈 소스 소프트웨어와 聯合宇宙(연합우주)(fediverse)의 熱烈(열렬)支持者(지지자)입니다.

저는 TypeScript() ActivityPub 서버 프레임워크인 @fedify 프로젝트와 싱글 유저() ActivityPub 마이크로블로그인 @hollo 프로젝트와 ActivityPub 봇 프레임워크인 @botkit 프로젝트의 製作者(제작자)이기도 합니다.

저는 ()아시아 言語(언어)(이른바 )와 유니코드에도 關心(관심)이 많습니다. 聯合宇宙(연합우주)에서는 國漢文混用體(국한문 혼용체)를 쓰고 있어요! 제게 韓國語(한국어)英語(영어), 日本語(일본어)로 말을 걸어주세요. (아니면, 漢文(한문)으로도!)

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee)'s post

こんにちは、私はソウルに住んでいる30代後半のオープンソースソフトウェアエンジニアで、自由・オープンソースソフトウェアとフェディバースの熱烈な支持者です。名前は洪 民憙ホン・ミンヒです。

私はTypeScript用のActivityPubサーバーフレームワークである「@fedify」と、ActivityPubをサポートする1人用マイクロブログである 「@hollo」と、ActivityPubのボットを作成する為のシンプルなフレームワークである「@botkit」の作者でもあります。

私は東アジア言語(いわゆるCJK)とUnicodeにも興味が多いです。日本語、英語、韓国語で話しかけてください。(または、漢文でも!)

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

@hongminhee@hollo.social · Reply to Emelia 👸🏻's post

@thisismissem Here it is! https://github.com/fedify-dev/fedify/tree/rfc9421

You can run the failing test by invoking:

deno task -f @fedify/fedify test --filter 'test vector' sig/http.test.ts
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

I'm implementing HTTP Message Signatures (RFC 9421) in Fedify, and although the signature base matches and the private–public key pair is correct, the signature verification keeps failing. I've wasted hours on this issue and still have no idea what's wrong. 😩

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

@hongminhee@hollo.social

@hollo 보통 네이버 사전에서 찾는데, 이제 보니 《표준국어대사전》에는 등재되어 있지 않고 《고려대 한국어대사전》에만 등재되어 있었네요…

쯔방 :yuri: :yurigarden: :garden:'s avatar
쯔방 :yuri: :yurigarden: :garden:

@pbzweihander@yuri.garden

음모를 꾸미는 논바이너리... 음모논... ​:ablobcatresonyance:

RE:
https://hollo.social/@hongminhee/0196a8fa-0113-7284-8830-af1f3e8268d1

땡상어 :spinny_cat_gay:'s avatar
땡상어 :spinny_cat_gay:

@thx@mustard.blog

음모론을 꾸미는 음모논 :blobcatevil:

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

@hongminhee@hollo.social · Reply to 洪 民憙 (Hong Minhee)'s post

아니… 《標準國語大辭典(표준국어대사전)》에 分明(분명)히 【陰謀論(음모논)】이 標題語(표제어)로 있는데 어째서 讀音(독음)이 〔음모논〕으로 달리는 거야… 😠

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

@hongminhee@hollo.social

여태까지 地下鐵(지하철)에서 다음 ()이 어딘지 알 수 없을 때마다 머릿속에서 온갖 陰謀論(음모논)을 떠올렸었는데… 이제라도 다음 ()常時(상시) 表示(표시)된다니 多幸(다행)이네. (내가 주로 떠올렸던 陰謀論(음모논)廣告(광고)를 더 많이 보게 하려고 다음 ()을 가끔만 表示(표시)한다는 것이었다.)

https://news.jtbc.co.kr/article/NB12245370

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

@hongminhee@hollo.social · Reply to 쯔방 :yuri: :yurigarden: :garden:'s post

@pbzweihander 사실 한국어는 언어 순화의 효험을 많이 본 쪽에 속하기 때문에 (50년대 초반까지만 해도 일상어였던 상당수의 일본어 잔재들이 지금은 거의 다 사라졌다든가, 맞춤법 규정와 외래어 표기법을 여러 차례 바꿨는데도 언중이 결과적으로 따라와 줬다든가) 오히려 언어 규범에 대한 부정적인 정서가 형성되어 있는 것 같기도 합니다. 🤔

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

@hongminhee@hollo.social · Reply to 쯔방 :yuri: :yurigarden: :garden:'s post

@pbzweihander 그렇군요. 일단 링크하신 글들의 설명이 크게 틀리지는 않다고 여겨지는데, 어떤 점이 문제라고 여겨지시는 걸까요? 제가 이해하기론 언어의 사회성은 어문 규범의 원칙이 아니라 언어의 자연적 성질을 기술한 것에 가까운 것 같거든요. 그러니까 「사회적으로 합의되어 있는 언어를 개인이 함부로 바꿔서는 안 된다」라기 보다는 「개인이 바꾸고 싶어도 바꾸기 힘들다」에 가까운 거죠. 다시 말해, 당위가 아니라 사실이라고 여겨집니다.

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

@hongminhee@hollo.social · Reply to 쯔방 :yuri: :yurigarden: :garden:'s post

@pbzweihander 음… 저는 언어의 사회성을 그런 식으로 배운 적이 없어서 잘 모르겠네요. 😅

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

@hongminhee@hollo.social · Reply to 쯔방 :yuri: :yurigarden: :garden:'s post

@pbzweihander 혹시 찾으시는 게 이런 걸까요?

https://www.rep.routledge.com/articles/thematic/language-social-nature-of/v-1

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

@hongminhee@hollo.social

和風 햄버그스테이크

오늘의 點心(점심)和風(화풍) 햄버그스테이크!

和風 햄버그스테이크
ALT text details和風 햄버그스테이크
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hackers.pub

Fedify에 RFC 9421을 구현하고 테스트 벡터랑 대조해 보는데, 시그니처 베이스도 일치하고 키도 동일함에도 서명이 맞지 않는다… 이거 테스트 벡터가 잘못된 거 아닌가 하는 생각이 슬금슬금 들 정도로 오래 쳐다보고 있는데 영 모르겠다. 😵

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

@hongminhee@hackers.pub

Hackers' Pub은 기본 Markdown 문법 외에 다양한 확장 문법을 지원합니다. TeX을 통한 수식, 각주, 경고 박스(admonitions), , Graphviz를 통한 도표, 코드 블록에서 특정 줄만 강조하기 등…

마땅한 기술 블로깅 플랫폼을 못 찾았다면, Hackers' Pub도 고려해 보세요!

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

@hongminhee@hollo.social

GNOME 48로 업그레이드했더니 UI에 쓰인 書體(서체)가 달라져서 適應(적응)이 안 된다…

sublimer@あすてろいどん鯖管's avatar
sublimer@あすてろいどん鯖管

@sublimer@mstdn.sublimer.me

氏名の入力欄、難しいこと考えるのやめて、一個のテキストボックスでいいんじゃないかなぁという気持ちと、後ろにつながっているシステムとかの制約で氏名を別で入力せざるを得ないとかの事情があるんだろうなぁという気持ちの両方がある

Masaki Hara's avatar
Masaki Hara

@qnighy@qnmd.info

最悪

MicrosoftのWebページで、「名前を確認しましょう」と表示されている画面のスクリーンショット。
「名」に「将己」、「姓」に「原」と漢字で記入されており、「この名前は短すぎます (最低2文字) というエラーが表示されている。
ALT text detailsMicrosoftのWebページで、「名前を確認しましょう」と表示されている画面のスクリーンショット。 「名」に「将己」、「姓」に「原」と漢字で記入されており、「この名前は短すぎます (最低2文字) というエラーが表示されている。
Clairement crevée's avatar
Clairement crevée

@Claire@sitedethib.com · Reply to Tak!'s post

@Tak @hongminhee it's the other way around, FEP-5624 pre-dates GTS' interaction policies but was never implemented anywhere and did not get much traction; the bulk of the discussion at the time was about who should control the reply policy (original post author or person you immediately reply to)

GTS decided to pick the second solution even if it's not necessarily the ideal one, because it's much simpler to implement

GTS' interaction policies were then refined with a lot of back-and-forth with Mastodon devs when we were working on quote posts (resulting in FEP-044F which re-use GTS' interaction policies)

maybe we should retire FEP-5624

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

@hongminhee@hackers.pub

After reviewing FEP-5624: Per-object reply control policies and GoToSocial's interaction policy spec, I find myself leaning toward the latter for long-term considerations, though both have merit.

FEP-5624 is admirably focused and simpler to implement, which I appreciate. However, 's approach seems to offer some architectural advantages:

  1. The three-tier permission model (allow/require approval/deny) feels more flexible than binary allow/deny
  2. Separating approval objects from interactions appears more secure against forgery
  3. The explicit handling of edge cases (mentioned users, post authors) provides clearer semantics
  4. The extensible framework allows for handling diverse interaction types, not just replies

I wonder if creating an that extracts GoToSocial's interaction policy design into a standalone standard might be worthwhile. It could potentially serve as a more comprehensive foundation for access control in .

This is merely my initial impression though. I'd be curious to hear other developers' perspectives on these approaches.

Renaud Chaput's avatar
Renaud Chaput

@renchap@oisaur.com

We are implementing the final version of RFC9421 (HTTP Signatures) in Mastodon, and would like to test this with other ActivityPub implementations.

Do you know of any AP implementations supporting both incoming (verification) and outgoing (signing) RFC9421 signatures, and if possible with support for the double-knocking mechanism as described in swicg.github.io/activitypub-ht (section 3.5)?

에스텔 뉴스계정's avatar
에스텔 뉴스계정

@transborder.bsky.social@bsky.brid.gy

[칼럼] 이재명을 핑계로 국민 위에 올라서려 하지 말라 2025년 05월 05일 17시 30분 newstapa.org/article/3vCxV

[칼럼] 이재명을 핑계로 국민 위에 올라서려 하지 말라

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

@hongminhee@hollo.social

@hollo ALLOW_HTML 설정을 켜 주셔야 합니다.

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

@hongminhee@hollo.social

Hackers' PubでのDOT言語(Graphviz)によるダイアグラム作成

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

@hongminhee@hackers.pub


Hackers' Pub의 숨겨진 기능 중 하나는 Graphviz의 DOT 언어를 지원한다는 것입니다. 예를 들어, 다음과 같은 다이어그램을 그릴 수 있습니다:

SimpleActivityPub server_a 서버 A (Mastodon) server_b 서버 B (Hackers' Pub) server_a->server_b ActivityStreams 데이터 전송 (HTTP POST) server_b->server_a 응답 및 상호작용 (HTTP POST)

Graphviz를 이용하는 법은 간단합니다. Markdown의 코드 블럭 문법 안에 DOT 언어로 다이어그램을 기술하신 뒤, 코드 블럭의 언어 태그에 graphviz를 붙이시면 됩니다. 위에서 예를 든 다이어그램은 Markdown에서 아래와 같이 쓰면 됩니다:

```graphviz
digraph SimpleActivityPub {
    graph [rankdir=LR, fontname="sans-serif", bgcolor="white"];
    node [fontname="sans-serif", shape=box, style="rounded,filled"];
    edge [fontname="sans-serif"];
    
    server_a [label="서버 A\n(Mastodon)", fillcolor="#AED6F1"];
    server_b [label="서버 B\n(Hackers' Pub)", fillcolor="#A3E4D7"];
    
    server_a -> server_b [label="ActivityStreams 데이터 전송\n(HTTP POST)", color="red"];
    server_b -> server_a [label="응답 및 상호작용\n(HTTP POST)", color="blue"];
}
```

참고로 Graphviz는 긴 게시글 뿐만 아니라 단문에서도 똑같이 지원합니다.

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

@hongminhee@hollo.social

農心(농심)에서 새로 나온 麻辣(마랄) 짜파게티를 먹어 보았는데, 맵기만 하고 얼얼한 맛은 크게 없었다. 나는 매운 걸 못 먹어서 좀 힘들었다. 다시 먹지는 않을 듯…

農心 麻辣 짜파게티
ALT text details農心 麻辣 짜파게티
なっかあ@絶対痩せる2025's avatar
なっかあ@絶対痩せる2025

@nakkaa@misskey.7ka.org

ブログを書いた。
おひとり様ActivityPub実装Holloを始めた

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

@hongminhee@hackers.pub

Hackers' Pubに興味はあるけれど、DMで招待状をお願いするのは気が引けるという方のために、Googleフォームを作成しました。メールアドレスを入力するだけで、Hackers' Pubの招待状をお送りします。たくさんのご応募をお待ちしています‼️

https://forms.gle/F3ETSDvaGmkSV2hh6



RE: https://hackers.pub/@hongminhee/0195cb17-5c14-7d9a-a163-84613df90481

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

@hongminhee@hackers.pub

Hackers' Pubは現在、韓国語中心のコミュニティが形成されていますが、日本語のコミュニティも拡大することを希望しています。Hackers' Pubは、まるでQiitaやZennの様なソフトウェア開発者の為のブログプラットフォームであると同時に、MisskeyやMastodonの様なマイクロブログプラットフォームでもあり、何よりもActivityPubをサポートしているので、Mastodonや Misskey等とも交流が出来ます。(このアカウントもHackers' Pubのアカウントです!)

Hackers' Pubに興味の有る方は、私にDMでメールアドレスをお知らせいただければ、招待状を送らせていただきます。 是非、ご参加をお待ちしております。宜しくお願いします。

kphrx's avatar
kphrx

@kPherox@pl.kpherox.dev

なおmastodon

> 実際、ActivityPubの開発過程では、このような問題を考慮してサーバー間でブロック活動を伝達しないよう明示的に設計されました。これはブロックした人がブロックされた人からの報復を受けるリスクを減らすためです。

Blueskyは Xの優れた代替となりうるが、フェディバースの代替ではない
https://hackers.pub/@hongminhee/2025/bluesky-a-good-alternative-to-x-not-to-the-fediverse/ja?lang=ja#0195c268-865c-73d7-ba2d-0d090fb5ad84--%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%93%E3%83%A5%E3%83%BC%E3%81%B8%E3%81%AE%E5%9F%B7%E7%9D%80%E3%81%A8%E4%B8%AD%E5%A4%AE%E9%9B%86%E6%A8%A9%E5%8C%96%E3%81%AE%E6%B7%B1%E5%8C%96
gorn@蒼象's avatar
gorn@蒼象

@gorn@toot.blue · Reply to 洪 民憙 (Hong Minhee)'s post

@hongminhee それは私も思ってる。Xの代替だけどFediverseにはなれない。

dansup's avatar
dansup

@dansup@mastodon.social

Introducing Popular Accounts v2 in the new FediDB ✨

new.fedidb.org/accounts

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

@hongminhee@hollo.social

DIYドラマを捨てよう:なぜActivityPubを一から構築するのではなくFedifyを使うべきなのか?

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

@hongminhee@hackers.pub


So, you're captivated by the fediverse—the decentralized social web powered by protocols like ActivityPub. Maybe you're dreaming of building the next great federated app, a unique space connected to Mastodon, Lemmy, Pixelfed, and more. The temptation to dive deep and implement ActivityPub yourself, from the ground up, is strong. Total control, right? Understanding every byte? Sounds cool!

But hold on a sec. Before you embark on that epic quest, let's talk reality. Implementing ActivityPub correctly isn't just one task; it's like juggling several complex standards while riding a unicycle… blindfolded. It’s hard.

That's where Fedify comes in. It's a TypeScript framework designed to handle the gnarliest parts of ActivityPub development, letting you focus on what makes your app special, not reinventing the federation wheel.

This post will break down the common headaches of DIY ActivityPub implementation and show how Fedify acts as the super-powered pain reliever, starting with the very foundation of how data is represented.

Challenge #1: Data Modeling—Speaking ActivityStreams & JSON-LD Fluently

At its core, ActivityPub relies on the ActivityStreams 2.0 vocabulary to describe actions and objects, and it uses JSON-LD as the syntax to encode this vocabulary. While powerful, this combination introduces significant complexity right from the start.

First, understanding and correctly using the vast ActivityStreams vocabulary itself is a hurdle. You need to model everything—posts (Note, Article), profiles (Person, Organization), actions (Create, Follow, Like, Announce)—using the precise terms and properties defined in the specification. Manual JSON construction is tedious and prone to errors.

Second, JSON-LD, the encoding layer, has specific rules that make direct JSON manipulation surprisingly tricky:

  • Missing vs. Empty Array: In JSON-LD, a property being absent is often semantically identical to it being present with an empty array. Your application logic needs to treat these cases equally when checking for values. For example, these two Note objects mean the same thing regarding the name property:
    // No name property
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ""
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "name": [],
      "content": ""
    }
  • Single Value vs. Array: Similarly, a property holding a single value directly is often equivalent to it holding a single-element array containing that value. Your code must anticipate both representations for the same meaning, like for the content property here:
    // Single value
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "Hello"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ["Hello"]
    }
  • Object Reference vs. Embedded Object: Properties can contain either the full JSON-LD object embedded directly or just a URI string referencing that object. Your application needs to be prepared to fetch the object's data if only a URI is given (a process called dereferencing). These two Announce activities are semantically equivalent (assuming the URIs resolve correctly):
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // Embedded objects:
      "actor": {
        "type": "Person",
        "id": "http://sally.example.org/",
        "name": "Sally"
      },
      "object": {
        "type": "Arrive",
        "id": "https://sally.example.com/arrive",
        /* ... */
      }
    }
    // Equivalent to:
    {
      "@context":
      "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // URI references:
      "actor": "http://sally.example.org/",
      "object": "https://sally.example.com/arrive"
    }

Attempting to manually handle all these vocabulary rules and JSON-LD variations consistently across your application inevitably leads to verbose, complex, and fragile code, ripe for subtle bugs that break federation.

Fedify tackles this entire data modeling challenge with its comprehensive, type-safe Activity Vocabulary API. It provides TypeScript classes for ActivityStreams types and common extensions, giving you autocompletion and compile-time safety. Crucially, these classes internally manage all the tricky JSON-LD nuances. Fedify's property accessors present a consistent interface—non-functional properties (like tags) always return arrays, functional properties (like content) always return single values or null. It handles object references versus embedded objects seamlessly through dereferencing accessors (like activity.getActor()) which automatically fetch remote objects via URI when needed—a feature known as property hydration. With Fedify, you work with a clean, predictable TypeScript API, letting the framework handle the messy details of AS vocabulary and JSON-LD encoding.

Challenge #2: Discovery & Identity—Finding Your Actors

Once you can model data, you need to make your actors discoverable. This primarily involves the WebFinger protocol (RFC 7033). You'd need to build a server endpoint at /.well-known/webfinger capable of parsing resource queries (like acct: URIs), validating the requested domain against your server, and responding with a precisely formatted JSON Resource Descriptor (JRD). This JRD must include specific links, like a self link pointing to the actor's ActivityPub ID using the correct media type. Getting any part of this wrong can make your actors invisible.

Fedify simplifies this significantly. It automatically handles WebFinger requests based on the actor information you provide through its setActorDispatcher() method. Fedify generates the correct JRD response. If you need more advanced control, like mapping user-facing handles to internal identifiers, you can easily register mapHandle() or mapAlias() callbacks. You focus on defining your actors; Fedify handles making them discoverable.

// Example: Define how to find actors
federation.setActorDispatcher(
  "/users/{username}",
  async (ctx, username) => { /* ... */ }
);
// Now GET /.well-known/webfinger?resource=acct:username@your.domain just works!

Challenge #3: Core ActivityPub Mechanics—Handling Requests and Collections

Serving actor profiles requires careful content negotiation. A request for an actor's ID needs JSON-LD for machine clients (Accept: application/activity+json) but HTML for browsers (Accept: text/html). Handling incoming activities at the inbox endpoint involves validating POST requests, verifying cryptographic signatures, parsing the payload, preventing duplicates (idempotency), and routing based on activity type. Implementing collections (outbox, followers, etc.) with correct pagination adds another layer.

Fedify streamlines all of this. Its core request handler (via Federation.fetch() or framework adapters like @fedify/express) manages content negotiation. You define actors with setActorDispatcher() and web pages with your framework (Hono, Express, SvelteKit, etc.)—Fedify routes appropriately. For the inbox, setInboxListeners() lets you define handlers per activity type (e.g., .on(Follow, ...)), while Fedify automatically handles validation, signature verification, parsing, and idempotency checks using its KV Store. Collection implementation is simplified via dispatchers (e.g., setFollowersDispatcher()); you provide logic to fetch a page of data, and Fedify constructs the correct Collection or CollectionPage with pagination.

// Define inbox handlers
federation.setInboxListeners("/inbox", "/users/{handle}/inbox")
  .on(Follow, async (ctx, follow) => { /* Handle follow */ })
  .on(Undo, async (ctx, undo) => { /* Handle undo */ });

// Define followers collection logic
federation.setFollowersDispatcher(
  "/users/{handle}/followers",
  async (ctx, handle, cursor) => { /* ... */ }
);

Challenge #4: Reliable Delivery & Asynchronous Processing—Sending Activities Robustly

Sending an activity requires more than a simple POST. Networks fail, servers go down. You need robust failure handling and retry logic (ideally with backoff). Processing incoming activities synchronously can block your server. Efficiently broadcasting to many followers (fan-out) requires background processing and using shared inboxes where possible.

Fedify addresses reliability and scalability using its MessageQueue abstraction. When configured (highly recommended), Context.sendActivity() enqueues delivery tasks. Background workers handle sending with automatic retries based on configurable policies (like outboxRetryPolicy). Fedify supports various queue backends (Deno KV, Redis, PostgreSQL, AMQP). For high-traffic fan-out, Fedify uses an optimized two-stage mechanism to distribute the load efficiently.

// Configure Fedify with a persistent queue (e.g., Deno KV)
const federation = createFederation({
  queue: new DenoKvMessageQueue(/* ... */),
  // ...
});
// Sending is now reliable and non-blocking
await ctx.sendActivity({ handle: "myUser" }, recipient, someActivity);

Challenge #5: Security—Avoiding Common Pitfalls

Securing an ActivityPub server is critical. You need to implement HTTP Signatures (draft-cavage-http-signatures-12) for server-to-server authentication—a complex process. You might also need Linked Data Signatures (LDS) or Object Integrity Proofs (OIP) based on FEP-8b32 for data integrity and compatibility. Managing cryptographic keys securely is essential. Lastly, fetching remote resources risks Server-Side Request Forgery (SSRF) if not validated properly.

Fedify is designed with security in mind. It automatically handles the creation and verification of HTTP Signatures, LDS, and OIP, provided you supply keys via setKeyPairsDispatcher(). It includes key management utilities. Crucially, Fedify's default document loader includes built-in SSRF protection, blocking requests to private IPs unless explicitly allowed.

Challenge #6: Interoperability & Maintenance—Playing Nicely with Others

The fediverse is diverse. Different servers have quirks. Ensuring compatibility requires testing and adaptation. Standards evolve with new Federation Enhancement Proposals (FEPs). You also need protocols like NodeInfo to advertise server capabilities.

Fedify aims for broad interoperability and is actively maintained. It includes features like ActivityTransformers to smooth over implementation differences. NodeInfo support is built-in via setNodeInfoDispatcher().

Challenge #7: Developer Experience—Actually Building Your App

Beyond the protocol, building any server involves setup, testing, and debugging. With federation, debugging becomes harder—was the message malformed? Was the signature wrong? Is the remote server down? Is it a compatibility quirk? Good tooling is essential.

Fedify enhances the developer experience significantly. Being built with TypeScript, it offers excellent type safety and editor auto-completion. The fedify CLI is a powerful companion designed to streamline common development tasks.

You can quickly scaffold a new project tailored to your chosen runtime and web framework using fedify init.

For debugging interactions and verifying data, fedify lookup is invaluable. It lets you inspect how any remote actor or object appears from the outside by performing WebFinger discovery and fetching the object's data. Fedify then displays the parsed object structure and properties directly in your terminal. For example, running:

$ fedify lookup @fedify-example@fedify-blog.deno.dev

Will first show progress messages and then output the structured representation of the actor, similar to this:

// Output of fedify lookup command (shows parsed object structure)
Person {
  id: URL "https://fedify-blog.deno.dev/users/fedify-example",
  name: "Fedify Example Blog",
  published: 2024-03-03T13:18:11.857Z, // Simplified timestamp
  summary: "This blog is powered by Fedify, a fediverse server framework.",
  url: URL "https://fedify-blog.deno.dev/",
  preferredUsername: "fedify-example",
  publicKey: CryptographicKey {
    id: URL "https://fedify-blog.deno.dev/users/fedify-example#main-key",
    owner: URL "https://fedify-blog.deno.dev/users/fedify-example",
    publicKey: CryptoKey { /* ... CryptoKey details ... */ }
  },
  // ... other properties like inbox, outbox, followers, endpoints ...
}

This allows you to easily check how data is structured or troubleshoot why an interaction might be failing by seeing the actual properties Fedify parsed.

Testing outgoing activities from your application during development is made much easier with fedify inbox. Running the command starts a temporary local server that acts as a publicly accessible inbox, displaying key information about the temporary actor it creates for receiving messages:

$ fedify inbox
✔ The ephemeral ActivityPub server is up and running: https://<unique_id>.lhr.life/
✔ Sent follow request to @<some_test_account>@activitypub.academy.
╭───────────────┬─────────────────────────────────────────╮
│ Actor handle: │ i@<unique_id>.lhr.life                  │
├───────────────┼─────────────────────────────────────────┤
│   Actor URI:  │ https://<unique_id>.lhr.life/i          │
├───────────────┼─────────────────────────────────────────┤
│  Actor inbox: │ https://<unique_id>.lhr.life/i/inbox    │
├───────────────┼─────────────────────────────────────────┤
│ Shared inbox: │ https://<unique_id>.lhr.life/inbox      │
╰───────────────┴─────────────────────────────────────────╯

Web interface available at: http://localhost:8000/

You then configure your developing application to send an activity to the Actor inbox or Shared inbox URI provided. When an activity arrives, fedify inbox only prints a summary table to your console indicating that a request was received:

╭────────────────┬─────────────────────────────────────╮
│     Request #: │ 2                                   │
├────────────────┼─────────────────────────────────────┤
│ Activity type: │ Follow                              │
├────────────────┼─────────────────────────────────────┤
│  HTTP request: │ POST /i/inbox                       │
├────────────────┼─────────────────────────────────────┤
│ HTTP response: │ 202                                 │
├────────────────┼─────────────────────────────────────┤
│       Details  │ https://<unique_id>.lhr.life/r/2    │
╰────────────────┴─────────────────────────────────────╯

Crucially, the detailed information about the received request—including the full headers (like Signature), the request body (the Activity JSON), and the signature verification status—is only available in the web interface provided by fedify inbox. This web UI allows you to thoroughly inspect incoming activities during development.

Screenshot of the Fedify Inbox web interface showing received activities and their details.
The Fedify Inbox web UI is where you view detailed activity information.

When you need to test interactions with the live fediverse from your local machine beyond just sending, fedify tunnel can securely expose your entire local development server temporarily. This suite of tools significantly eases the process of building and debugging federated applications.

Conclusion: Build Features, Not Plumbing

Implementing the ActivityPub suite of protocols from scratch is an incredibly complex and time-consuming undertaking. It involves deep dives into multiple technical specifications, cryptographic signing, security hardening, and navigating the nuances of a diverse ecosystem. While educational, it dramatically slows down the process of building the actual, unique features of your federated application.

Fedify offers a well-architected, secure, and type-safe foundation, handling the intricacies of federation for you—data modeling, discovery, core mechanics, delivery, security, and interoperability. It lets you focus on your application's unique value and user experience. Stop wrestling with low-level protocol details and start building your vision for the fediverse faster and more reliably. Give Fedify a try!

Getting started is straightforward. First, install the Fedify CLI using your preferred method. Once installed, create a new project template by running fedify init your-project-name.

Check out the Fedify tutorials and Fedify manual to learn more. Happy federating!

← Newer
Older →