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

洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social

1,109 following1,901 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 메인테이너. , , , 等으로 自由 소프트웨어 만듦.

()

Pinned

@hongminhee@hollo.social

Hello! I'm Hong Minhee (洪 民憙), an open source software engineer in my late 30s, living in Seoul, Korea. I'm bisexual and non-binary (they/them), and an enthusiastic advocate of free/open source software and the fediverse.

I work full-time on @fedify, an ActivityPub server framework in TypeScript, funded by @sovtechfund. I'm also the creator of @hollo, a single-user ActivityPub microblog; @botkit, an ActivityPub bot framework; Hackers' Pub, a fediverse platform for software developers; and LogTape, a logging library for JavaScript and TypeScript.

I have a long interest in East Asian languages (CJK) and Unicode. I post mostly in English here, though occasionally in Japanese or in mixed-script Korean (國漢文混用體), a traditional writing style that interleaves Chinese characters with the native Korean alphabet. Wanting to write in that style was actually one of the reasons I joined the fediverse. Feel free to talk to me in English, Korean, Japanese, or even Literary Chinese!

en.wikipedia.org

Korean mixed script - Wikipedia

Pinned

はじめまして!ソウル在住の30代後半のオープンソースソフトウェアエンジニア、洪 民憙ホン・ミンヒと申します。バイセクシュアル(bisexual)・ノンバイナリー(non-binary)で、自由・オープンソースソフトウェア(F/OSS)とフェディバース(fediverse)の熱烈な支持者です。

STF(@sovtechfund)の支援を受け、TypeScript用ActivityPubサーバーフレームワーク「@fedify」の開発に専念しています。他にも、おひとり様向けのActivityPubマイクロブログ「@hollo」、ActivityPubボットフレームワーク「@botkit」、ソフトウェア開発者向けフェディバースプラットフォームHackers' Pub、JavaScript・TypeScript用ロギングライブラリLogTapeなどの制作者でもあります。

東アジア言語(いわゆるCJK)とUnicodeにも興味があります。このアカウントでは主に英語で投稿していますが、時々日本語や国漢文混用体(漢字ハングル混じり文)の韓国語でも書いています。実はこの文体で書きたくてフェディバースを始めた、という経緯もあります。日本語、英語、韓国語、漢文でも気軽に話しかけてください!

speakerdeck.com

国漢文混用体からHolloまで

本発表では、韓国語の「国漢文混用体」(漢字ハングル混じり文)を自分のフェディバース投稿に実装したいという小さな目標から始まった旅路を共有します。 この目標を達成するために、ActivityPubのJSON-LDの複雑さやHTTP Signatures、WebFingerなどの仕様を理解する必要性に…

Pinned

安寧(안녕)하세요! 저는 서울에 살고 있는 30() 後半(후반)의 오픈 소스 소프트웨어 엔지니어 洪民憙(홍민희)입니다. 兩性愛者(양성애자)(bisexual)이자 논바이너리(non-binary)이며, 自由(자유)·오픈 소스 소프트웨어(F/OSS)와 聯合宇宙(연합우주)(fediverse)의 熱烈(열렬)支持者(지지자)이기도 합니다.

STF(@sovtechfund)의 支援(지원)을 받아 TypeScript() ActivityPub 서버 프레임워크 @fedify 開發(개발)專業(전업)으로 ()하고 있습니다. 그 ()에도 싱글 유저() ActivityPub 마이크로블로그 @hollo, ActivityPub 봇 프레임워크 @botkit, 소프트웨어 開發者(개발자)를 위한 聯合宇宙(연합우주) 플랫폼 Hackers' Pub, JavaScript·TypeScript() 로깅 라이브러리 LogTape ()製作者(제작자)이기도 합니다.

()아시아 言語(언어)(이른바 CJK)와 Unicode에도 關心(관심)이 많습니다. 이 計定(계정)에서는 ()英語(영어)로 포스팅하지만, 때때로 日本語(일본어)國漢文混用體(국한문 혼용체) 韓國語(한국어)로도 씁니다. 聯合宇宙(연합우주)에 오게 된 動機(동기) () 하나가 바로 國漢文混用體(국한문 혼용체)로 글을 쓰고 싶었기 때문이기도 하고요. 韓國語(한국어), 英語(영어), 日本語(일본어), 아니면 漢文(한문)으로도 말을 걸어주세요!

logtape.org

LogTape

Unobtrusive logging library with zero dependencies—library-first design for Deno, Node.js, Bun, browsers, and edge functions

@hongminhee@hackers.pub

어째서 ActivityPub을 바닥부터 구현하는 것보다 Fedify를 쓰는 게 여러모로 나은지에 대한 글(영어)을 썼습니다.

hackers.pub

Ditch the DIY Drama: Why Use Fedify Instead of Building ActivityPub from Scratch?

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 actorsfederation.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 handlersfederation.setInboxListeners("/inbox", "/users/{handle}/inbox") .on(Follow, async (ctx, follow) => { /* Handle follow */ }) .on(Undo, async (ctx, undo) => { /* Handle undo */ });// Define followers collection logicfederation.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-blockingawait 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.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!

@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!

fedify.dev

Federation | Fedify

The Federation object is the main entry point of the Fedify library. This section explains the key features of the Federation object.

@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!

fedify.dev

Federation | Fedify

The Federation object is the main entry point of the Fedify library. This section explains the key features of the Federation object.

@FediTips@social.growyourown.services

@pbzweihander さんが「フェディバースの日」専用のウェブサイトfediday.orgを作成した事をお知らせします。このサイトでは、韓国語圏のフェディバースコミュニティから生まれた4月11日を「連合型フェディバースの日」として祝っています。サイトでは、この取り組みの背景やフェディバースの重要な記念日についての情報も掲載されています。特別な日を追加したい場合は、GitHubリポジトリを通じて貢献することができます!

github.com

GitHub - pbzweihander/fediday.org: The Federated Fediverse Day

The Federated Fediverse Day. Contribute to pbzweihander/fediday.org development by creating an account on GitHub.

@hongminhee@hollo.social · Reply to 맹꽁이

@root 2人稱(인칭) 自體(자체)無禮(무례)하게 느껴지기 쉬워서 그런 게 큰 것 같아요. @sunwoo1524 님 말씀처럼 「貴下(귀하)」라는 말이 있긴 한데 口語(구어)로는 안 쓰입니다. 口語(구어)에서는 「先生(선생)님」 같은 말을 많이 쓰는 것 같아요.

Excited to share that @pbzweihander has created fediday.org—a new website dedicated to Fediverse Day! The site celebrates April 11th as Federated Fediverse Day, which originated from conversations in the Korean fediverse community. Visit the site to learn about the background of this initiative and discover other important dates in the fediverse calendar. If you'd like to add a special date, you can contribute through the GitHub repository!

github.com

GitHub - pbzweihander/fediday.org: The Federated Fediverse Day

The Federated Fediverse Day. Contribute to pbzweihander/fediday.org development by creating an account on GitHub.

Fetching remote objects or actors often involves handling lookups, content negotiation, and then parsing potentially untyped JSON.

With , it's much simpler: use Context.lookupObject(). Pass it a URI (e.g., https://instance.tld/users/alice) or a handle (e.g., @alice@instance.tld), and Fedify handles the lookup and content negotiation automatically.

The real power comes from the return value: a type-safe Activity Vocabulary object, not just raw JSON. This allows you to confidently access properties and methods directly. For example, you can safely traverse account moves using .getSuccessor() like this:

let actor = await ctx.lookupObject("@alice@instance.tld");
while (isActor(actor)) {
  const successor = await actor.getSuccessor();
  if (successor == null) break;
  actor = successor;
}
// actor now holds the latest account after moves

This is readily available in handlers where the Context object is provided (like actor dispatchers or inbox listeners).

Focus on your app's logic, not protocol boilerplate!

Learn more: https://fedify.dev/manual/context#looking-up-remote-objects

fedify.dev

Context | Fedify

The Context object is a container that holds the information of the current request. This section explains the key features of the Context object.

Excited to share that @pbzweihander has created fediday.org—a new website dedicated to Fediverse Day! The site celebrates April 11th as Federated Fediverse Day, which originated from conversations in the Korean fediverse community. Visit the site to learn about the background of this initiative and discover other important dates in the fediverse calendar. If you'd like to add a special date, you can contribute through the GitHub repository!

github.com

GitHub - pbzweihander/fediday.org: The Federated Fediverse Day

The Federated Fediverse Day. Contribute to pbzweihander/fediday.org development by creating an account on GitHub.

@hongminhee@hollo.social
@hongminhee@hackers.pub

Hackers' Pub은 검색에서 몇 가지 기본적인 문법들을 지원하고 있습니다. 문서화되지 않았기 때문에 한 번 정리해 봅니다.

문법 설명 예시
" 키워드 " 따옴표 안에 들어간 문자열을 띄어쓰기를 포함하여 찾습니다.
대소문자는 구분하지 않습니다.
(따옴표 안에 따옴표를 넣으려면 \"와 같이 이스케이프.)
"Hackers' Pub"
from: 핸들 해당 사용자가 쓴 콘텐츠만 추립니다. from:hongminhee
from:hongminhee@hollo.social
lang: ISO 639-1 해당 언어로 쓰여진 콘텐츠만 추립니다. lang:ko
# 태그 해당 태그가 달린 콘텐츠만 추립니다.
대소문자는 구분하지 않습니다.
#Fedify
조건 조건 띄어쓰기 양 옆의 조건을 모두 만족하는 콘텐츠만 추립니다(논리곱). "Hackers' Pub" lang:ko
조건 OR 조건 OR 연산자 양 옆의 조건 중 하나라도 만족하는 콘텐츠를 추립니다(논리합). 해커즈퍼브 OR "Hackers' Pub" lang:ko
( 조건 ) 괄호 안의 연산자들을 먼저 결합합니다. (해커즈퍼브 OR 해커즈펍 OR 해커스펍) lang:ko

구체적인 동작 방식은 Hackers' Pub 소스 코드를 보시면 더 자세히 알 수 있습니다.

github.com

hackerspub/models/search.ts at fbb4839339c4f15b68818ae63066951a64b29368 · hackers-pub/hackerspub

ActivityPub-enabled social network for hackers. Contribute to hackers-pub/hackerspub development by creating an account on GitHub.

@deno_land@fosstodon.org

🚀 Deno v2.2.9 is released!

🦕 Improves Node compat: fs.FileHandle.createReadStream, Buffer.copyBytesFrom, spawnSync.input
🦕 fixes windows icons for deno compile
🦕supports backticks in deno task
🦕 Faster npm installs

release notes here:
github.com/denoland/deno/relea

Release v2.2.9 · denoland/deno

Deno v2.2.9 / 2025.04.11 deno upgrade deno compile fixes Two fixes make the deno compile tool more robust. First, a bug causing a panic when a node_modules directory was included during compilatio...

@skk@mas.to
@hongminhee@hollo.social

The Korean fediverse community is proposing to establish April 11th as “Fediverse Day” (聯合宇宙(연합우주)의 날). Since this idea emerged today in conversations among Korean fediverse users, they've suggested making today's date the official celebration going forward.

We're sharing this initiative with the broader fediverse community and would love to know if you'd support and participate in an annual Fediverse Day!

uri.life

숲 for ㉰짐。 (@dazeemdas@uri.life)

오늘은 연합우주의 날입니다

:_ko::_ko::_a::_ni::_lya::_n:
@cocoa_vrc@misskey.io