Profile img

Hi, I'm who's behind Fedify, Hollo, BotKit, and this website, Hackers' Pub! My main account is at @hongminhee洪 民憙 (Hong Minhee).

Fedify, Hollo, BotKit, 그리고 보고 계신 이 사이트 Hackers' Pub을 만들고 있습니다. 제 메인 계정은: @hongminhee洪 民憙 (Hong Minhee).

FedifyHolloBotKit、そしてこのサイト、Hackers' Pubを作っています。私のメインアカウントは「@hongminhee洪 民憙 (Hong Minhee)」に。

Website
hongminhee.org
GitHub
@dahlia
Hollo
@hongminhee@hollo.social
DEV
@hongminhee
velog
@hongminhee
Qiita
@hongminhee
Zenn
@hongminhee
Matrix
@hongminhee:matrix.org
X
@hongminhee

Stop writing CLI validation. Parse it right the first time.

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

This post introduces Optique, a new library created to address the pervasive problem of repetitive and often messy validation code in CLI tools. The author was motivated by the observation that nearly every CLI tool reinvents the wheel with similar validation patterns for dependent options, mutually exclusive options, and environment-specific requirements. Optique leverages parser combinators and TypeScript's type inference to ensure that CLI arguments are parsed directly into valid configurations, eliminating the need for manual validation. By describing the desired CLI configuration with Optique, TypeScript automatically infers the types and constraints, catching potential bugs at compile time. The author shares their experience of deleting large chunks of validation code and simplifying refactoring tasks. Optique aims to provide a more robust and maintainable approach to CLI argument parsing, potentially saving developers from writing the same validation logic repeatedly.

Read more →
19
3
1

Optique 0.4.0: Better help, rich docs, and Temporal support

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Optique 0.4.0 introduces enhancements to streamline CLI development in TypeScript. This release focuses on improving help text organization through labeled merge groups and a new `group()` combinator, making complex CLIs more user-friendly by organizing options under clear sections. Comprehensive documentation support is added via the `run()` function, allowing brief descriptions, detailed explanations, and footers without altering parser definitions. The update also includes Temporal API support with the `@optique/temporal` package, enabling type-safe parsing for dates, times, and time zones. Improved type inference for `merge()` and `tuple()` combinators enhances type safety, alongside minor breaking changes. These updates aim to make CLI construction more intuitive and maintainable, offering developers greater control over user experience and code structure.

Read more →
5

Optique 0.3.0: Dependent options and flexible composition

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Optique 0.3.0 introduces several enhancements aimed at simplifying the development of complex CLI applications. This release focuses on expanding parser flexibility and refining the help system, incorporating valuable community feedback. Key updates include the introduction of required Boolean flags using the new `flag()` parser, more flexible type defaults in `withDefault()` to support union types, and an extended `or()` capacity that now supports up to 10 parsers. The `merge()` combinator has also been enhanced to work with any object-producing parser, and context-aware help is now available through the `longestMatch()` combinator. Additionally, version display support has been added to both `@optique/core` and `@optique/run`, along with structured output functions for consistent terminal formatting. These improvements collectively provide developers with more powerful tools for building intuitive and feature-rich command-line interfaces.

Read more →
3

Optique: 타입 안전한 CLI 파서 컴비네이터

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 글에서는 Haskell의 `optparse-applicative`와 TypeScript의 Zod에서 영감을 받아 제작된 새로운 CLI 파서 라이브러리인 Optique를 소개합니다. Optique는 파서 컴비네이터를 활용하여 CLI의 구조를 레고 블록처럼 조립할 수 있게 해줍니다. `option()`, `optional()`, `multiple()`, `or()`, `object()`, `constant()`, `command()`, `argument()` 등의 다양한 파서와 컴비네이터를 통해 복잡한 CLI 구조를 유연하게 정의할 수 있습니다. 특히, `or()`와 `object()` 컴비네이터를 사용하여 상호 배타적인 옵션이나 서브커맨드를 쉽게 구현하는 방법을 예제를 통해 설명합니다. Optique는 단순한 CLI 파서 역할에 집중하고 있어 모든 기능을 제공하지는 않지만, 복잡한 CLI 구조를 표현하는 데 유용하며, 소개 문서와 튜토리얼을 통해 더 자세한 내용을 확인할 수 있습니다.

Read more →
13
2
2

Upyo 0.2.0 Release Notes

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Upyo 0.2.0 has been released, introducing new features to this cross-runtime email library that supports Node.js, Deno, Bun, and edge functions. The latest version expands its capabilities with Amazon SES transport support, enabling AWS Signature v4 authentication and session-based authentication. Additionally, comprehensive OpenTelemetry integration has been added, offering distributed tracing, metrics collection, and error classification without altering existing code. The OpenTelemetry transport automatically instruments email operations, tracking delivery rates and latency, and integrates with existing OpenTelemetry infrastructure. Community feedback is encouraged to further improve Upyo, whether through testing the new Amazon SES transport, implementing OpenTelemetry, or contributing to the GitHub repository. This release enhances Upyo's utility by providing more transport options and robust observability features, making it a valuable tool for developers needing reliable email sending across various environments.

Read more →
4

청개구리 스택 찬가

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 글은 저자가 기술 스택을 선택할 때 주류를 따르지 않고 대안적인 기술을 선택하는 경향, 즉 "청개구리 스택"을 추구하는 경험을 공유합니다. 청개구리 스택은 사용자가 적어 문제 해결에 어려움이 있을 수 있지만, 기술에 대한 깊이 있는 이해와 오픈 소스 기여 기회를 제공합니다. 또한, 후발주자로서 대안적인 설계를 통해 정석 스택보다 나은 이해를 제공할 수 있습니다. 여러 부품을 직접 조립하는 과정은 번거롭지만 각 기술에 대한 깊은 이해를 얻을 수 있게 합니다. 저자는 오늘의 정석 스택도 과거에는 청개구리 스택이었을 수 있음을 지적하며, LLM 시대에도 청개구리 스택이 주는 배움의 기회는 여전할 것이라고 주장합니다. Stack Overflow에 답이 없는 길을 걸으며 얻는 깨달음은 온전히 자신의 것이 될 것이라는 메시지를 전달하며, 독자들에게도 주체적인 기술 선택과 도전을 권장합니다.

Read more →
29
1
3

OSSCA: Fedify 프로젝트 기여자들을 위한 안내

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 글은 오픈 소스 컨트리뷰션 아카데미 참여자, 더 나아가 Fedify 프로젝트에 기여하고자 하는 모든 이들을 위한 안내서입니다. Fedify 프로젝트 참여를 위한 준비 사항과 소통 채널, 개발 환경 설정, 그리고 프로젝트 구조에 대한 이해를 돕는 것을 목표로 합니다. 먼저 Fedify Discord 서버에 참여하여 자기소개를 하고, 연합우주(fediverse)에 대한 기본적인 이해를 쌓기 위해 계정을 만들어보는 과제가 주어집니다. JavaScript와 TypeScript에 대한 간략한 소개와 함께, Fedify가 ActivityPub 프레임워크로서 연합우주 SNS 소프트웨어 개발을 쉽게 만들어주는 도구임을 설명합니다. 저장소를 포크하고 클론하는 방법, Node.js, Deno, Bun 등 다양한 런타임 환경 설정 방법, 그리고 Visual Studio Code를 활용한 개발 환경 구성 방법을 상세히 안내합니다. 마지막으로, Fedify 저장소의 구조와 린트, 테스트 실행 방법을 소개하며, 기여할 일감을 찾는 방법과 추가 정보 링크를 제공합니다. 이 글을 통해 독자는 Fedify 프로젝트에 실질적으로 기여하기 위한 첫걸음을 내딛을 수 있으며, 오픈 소스 기여에 대한 자신감을 얻을 수 있습니다.

Read more →
10
0
1

나만의 연합우주 마이크로블로그 만들기

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 튜토리얼은 Fedify를 사용하여 ActivityPub 프로토콜을 구현하는 마이크로블로그를 만드는 과정을 안내합니다. Fedify는 연합 서버 앱 개발의 복잡성을 줄이고, 개발자가 비즈니스 로직에 집중할 수 있도록 돕는 TypeScript 라이브러리입니다. 튜토리얼에서는 Node.js, npm, Hono 등의 개발 환경을 설정하고, SQLite 데이터베이스를 구축하여 계정 생성, 프로필 페이지, 액터 구현, 암호 키 관리, 팔로우 기능, 게시물 작성 및 타임라인 구현 등 마이크로블로그의 핵심 기능을 단계별로 구현합니다. 특히 ActivityPub.Academy 서버를 활용하여 실제 연합우주 환경에서의 연동을 테스트하고, Mastodon과의 호환성을 확인합니다. 마지막으로, 보안 및 기능 개선을 위한 추가 과제를 제시하며, 독자가 프로젝트를 확장할 수 있도록 안내합니다. 이 튜토리얼을 통해 독자는 Fedify를 활용하여 ActivityPub 기반의 분산 소셜 네트워크 서비스를 구축하는 기본적인 이해를 얻을 수 있습니다.

Read more →
6

How to pass the invisible

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

This post explores the enduring challenge in software programming of how to pass invisible contextual information, such as loggers or request contexts, through applications without cumbersome explicit parameter passing. It examines various approaches throughout history, including dynamic scoping, aspect-oriented programming (AOP), context variables, monads, and effect systems. Each method offers a unique solution, from the simplicity of dynamic scoping in early Lisp to the modularity of AOP and the type-safe encoding of effects in modern functional programming. The post highlights the trade-offs of each approach, such as the unpredictability of dynamic scoping or the complexity of monad transformers. It also touches on how context variables are used in modern asynchronous and parallel programming, as well as in UI frameworks like React. The author concludes by noting that the art of passing the invisible is an eternal theme in software programming, and this post provides valuable insights into the evolution and future directions of this critical aspect of software architecture.

Read more →
11
1
0

If you're building a JavaScript library and need logging, you'll probably love LogTape

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape offers a novel approach to logging in JavaScript libraries, designed to provide diagnostic capabilities without imposing choices on users. Unlike traditional methods such as using debug packages or custom logging systems, LogTape operates on a "library-first design" where logging is transparent and only activated when configured. This eliminates the fragmentation problem of managing multiple logging systems across different libraries. With zero dependencies and support for both ESM and CommonJS, LogTape ensures minimal impact on users' projects, avoiding dependency conflicts and enabling tree shaking. Its universal runtime support and efficient performance make it suitable for various environments. By using a hierarchical category system, LogTape prevents namespace collisions, offering a seamless developer experience with TypeScript support and structured logging patterns. LogTape provides adapters for popular logging libraries like Winston and Pino, bridging the transition for users invested in other systems. Ultimately, LogTape offers a way to enhance library capabilities while respecting users' preferences and existing choices, making it a valuable consideration for library authors.

Read more →
10

Announcing LogTape 1.0.0

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape 1.0.0 has been released, marking a significant milestone for this zero-dependency logging library designed for the modern JavaScript ecosystem. This release emphasizes API stability and introduces high-performance features such as non-blocking sinks for console, stream, and file logging, along with the `fromAsyncSink()` function for integrating asynchronous logging operations. New sink integrations include packages for AWS CloudWatch Logs and Windows Event Log, enhancing LogTape's versatility. The update also brings a visually appealing console logging experience with the `@logtape/pretty` package, and seamless integration with existing Winston or Pino setups through adapter packages. Key developer experience enhancements include programmatic access to log levels and improved browser compatibility. LogTape 1.0.0 streamlines logging infrastructure with a comprehensive package ecosystem, offering specialized packages for various logging needs. This release provides a stable and mature logging solution, making it easier to manage and optimize logging in JavaScript applications.

Read more →
11

LogTape 0.12.0 Release Notes

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape, a zero-dependency logging library for JavaScript and TypeScript, has released version 0.12.0 with several enhancements. The update introduces a new `trace` log level for more granular debugging and improves file sink performance through configurable buffering. A significant addition is the `@logtape/syslog` package, enabling log message transmission to syslog servers using RFC 5424. The update also includes `Logger.warning()` as an alias for `Logger.warn()` for consistency. Furthermore, all LogTape packages now share unified versioning for better compatibility. The build infrastructure has been migrated from `dnt` to `tsdown`, enhancing compatibility with modern JavaScript toolchains and improving build times. This release optimizes logging capabilities and ensures smoother integration with various JavaScript runtimes.

Read more →
9

Hackers' Pub의 게시글에 번역 힌트 주기

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Hackers' Pub의 게시글 번역 기능에서 LLM의 오역 가능성을 줄이기 위해, Markdown 내 HTML 주석을 활용한 특별한 지침 제공 방법을 소개합니다. 게시글 상단에 `<!--`와 `-->` 사이에 LLM 번역가를 위한 지침을 작성하여 제목 지정, 특정 용어 사용 등의 구체적인 요청을 할 수 있습니다. 이는 LLM에게 맥락을 제공하여 번역 품질을 향상시키는 데 도움을 줍니다. 저자가 직접 번역 맥락을 설정함으로써 LLM 번역의 정확성을 높일 수 있다는 점에서 유용한 팁입니다.

Read more →
4

“조용한 연합우주” 문제를 해결하는 두 가지 접근법: 대화 백필링 메커니즘

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 글은 연합우주(fediverse)에서 발생하는 "조용한 연합우주" 문제, 즉 대화의 일부만 보이는 현상의 원인과 해결책을 탐구합니다. ActivityPub 프로토콜의 분산 특성으로 인해 대화가 여러 서버에 분산되어 저장되면서 발생하는 이 문제를 해결하기 위해, 답글 트리 크롤링과 컨텍스트 소유자 기반 방식이라는 두 가지 주요 접근법을 제시합니다. 답글 트리 크롤링은 모든 답글을 순차적으로 가져오는 방식이지만 네트워크 취약성과 작업량 증가의 단점이 있고, 컨텍스트 소유자 방식은 대화의 원 작성자가 대화 내용을 관리하는 중앙화된 접근법이지만 컨텍스트 소유자에 대한 의존성이 높다는 단점이 있습니다. 또한, 모더레이션 패러다임의 충돌과 상위 전파 누락 문제와 같은 논쟁점을 지적하며, 주기적 크롤링, 사용자 트리거, 멘션 기반 백필과 같은 추가적인 백필 메커니즘을 소개합니다. 마지막으로, FEP 수렴 논의와 구현체 간 협력 현황을 통해 향후 개발 방향으로 하이브리드 접근법의 표준화를 제시하며, 다중 전략 구현, 리소스 관리, 모니터링 및 로깅의 모범 사례 가이드라인을 제시합니다. 이 글은 연합우주가 더욱 풍부하고 연결된 소셜 네트워크로 발전하기 위한 노력과 사용자 경험 개선의 중요성을 강조합니다.

Read more →
7
0
0

Fedify 1.6

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Fedify 1.6.1がリリースされ、Cloudflare Workersへの対応やセキュリティ互換性の向上が図られました。サーバーレス環境でのActivityPubアプリケーション実行を可能にするため、Cloudflare KV APIを利用した`WorkersKvStore`や、Cloudflare Queuesを活用した`WorkersMessageQueue`が導入されています。また、`FederationBuilder`クラスと`createFederationBuilder()`関数により、フェデレーションの遅延インスタンス化がサポートされ、コード構成の改善やCloudflare Workersとの互換性が向上しました。さらに、最新のHTTP Message Signatures標準(RFC 9421)を実装し、レガシー実装との互換性を保つためのダブルノック機構も導入されています。WebFinger機能の強化やContext APIの改善も行われ、開発者はより柔軟なリクエスト処理やデータフロー管理が可能になります。このリリースは、フェディバースにおける幅広い互換性を維持しつつ、デプロイメントの選択肢を広げ、新たなActivityPubセキュリティ標準に対応するための重要な一歩です。

Read more →
2

LogTape 0.11.0 release notes

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape 0.11.0 introduces enhanced structured logging capabilities and a new JSON Lines formatter, improving how developers handle logs across JavaScript runtimes. The update allows direct object logging, where structured data can be logged by passing an object as the first argument to any log method, and introduces a universal property interpolation with `{*}`. This placeholder streamlines the inclusion of all properties from structured data without explicitly naming each one. Additionally, the new JSON Lines formatter outputs each log record as a JSON object on a separate line, ideal for log aggregation systems and analysis tools, with customizable options for category separation, message formatting, and properties handling. These enhancements aim to make logs more searchable and analyzable, reduce boilerplate, and improve integration with log management systems, all while maintaining backward compatibility and LogTape's zero-dependency promise.

Read more →
4

LogTape 0.10.0 Released

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape 0.10.0 is now available, bringing enhancements to security, flexibility, and usability for this zero-dependency JavaScript logging library. A key addition is the @logtape/redaction package, which helps protect sensitive information using pattern-based and field-based redaction techniques. This package includes built-in patterns for common sensitive data types like credit card numbers and JWTs. The update also introduces improvements to timestamp formatting, allowing users to omit timestamps from formatted messages, and a lazy file sink option to improve resource utilization. Additionally, the configure() function now detects and prevents duplicate logger configurations. Thanks to external contributions, LogTape 0.10.0 offers more control over log output and improved resource management. Upgrade to this version to leverage these new features and improvements, enhancing your logging capabilities.

Read more →
3

Why LogTape Should Be Your Go-To Logging Library for JavaScript/TypeScript

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LogTape is a modern JavaScript and TypeScript logging library distinguished by its simplicity, flexibility, and broad runtime compatibility. One of its key advantages is its zero-dependency footprint, which reduces bundle size and enhances stability, making it ideal for both applications and libraries. LogTape supports multiple JavaScript runtimes, including Node.js, Deno, Bun, web browsers, and edge functions, ensuring consistent logging across different environments. Its hierarchical category system allows for fine-grained control and targeted filtering of logs, while structured logging enables improved searchability and data analysis. The library also offers simple extension mechanisms for creating custom sinks and filters with minimal boilerplate. Designed with library authors in mind, LogTape allows libraries to provide logging output without imposing specific configurations on users. With features like explicit and implicit contexts, LogTape facilitates richer logging by adding consistent properties across multiple log messages, making it easier to trace requests through a system. This post highlights LogTape's unique combination of features that address real-world development challenges, making it a valuable tool for any JavaScript or TypeScript project.

Read more →
6

Hackers' Pub 업데이트: LLM 기반의 게시글 번역 기능

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LLM 기반의 게시글 번역 기능이 추가되었습니다. 우선, 자신이 쓴 게시글이 LLM을 이용해 번역되는 것을 허용하려면, 게시글 공개 설정에서 “LLM 기반 자동 번역 허용” 옵션을 켜 주셔야 합니다. 기존 게시글은 모두 이 옵션이 꺼져 있습니다만, 새로 쓰는 게시글의 경우 기본적으로 켜져 있습니다.

한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션

위와 같이 옵션을 켜 준 게시글은 위쪽에 다음과 같이 “다른 언어로 읽기” 메뉴가 표시되게 됩니다. 이 메뉴에 나오는 언어 목록은 언어 설정에서 정할 수 있습니다.

게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)

이 중에서 이미 번역이 완료된 언어는 바로 표시되지만, 아직 번역이 완료되지 않은 언어의 경우, 아래와 같이 기다리라는 메시지가 뜨게 됩니다. 게시글의 분량에 따라 번역 시간은 차이가 나지만, 짧으면 30초에서 길면 5분 정도 걸립니다.

게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”

번역이 완료되면, 아래와 같이 메시지가 바뀝니다.

게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”

번역 기능은 제가 Hackers' Pub을 맨 처음 구상할 때부터 핵심 기능으로 고려하고 있던 것이었습니다. 소프트웨어 프로그래머로서 일정 수준 이상 성장하기 위해서는 반드시 영어를 배워야만 하는 불합리함이나 그리고 일본어나 중국어 등 영어가 아닌 언어로 쓰인 다양한 자료에 대부분의 외국인은 접근하지 못한다는 아쉬움을 오래 전부터 느꼈기 때문입니다. 다행히 얼마 전부터 LLM의 번역 품질이 아주 좋아졌고, 이를 활용하여 꽤 괜찮은 품질의 번역 기능을 Hackers' Pub 같은 작은 웹사이트에서도 구현할 수 있게 되었네요.

참고로 현재 번역에 쓰이는 모델은 Claude Sonnet 3.7입니다. 저렴하다고는 할 수 없는 모델인데요. 시범적으로 운영해 보고, 비용이 너무 부담된다고 여겨지면 Gemini 2.5 Flash 같은 다른 모델로 전환하는 것도 고려하고 있습니다.

아무튼, 모처럼 추가한 번역 기능이니 많은 분들이 유용함을 누리셨으면 좋겠습니다.

아래는 제가 샘플로 미리 만들어 둔 번역본들입니다:

  • Ditch the DIY Drama: Why Use Fedify Instead of Building ActivityPub from Scratch? (영어) → 〈DIY 드라마는 그만: 왜 ActivityPub을 처음부터 구축하는 대신 Fedify를 사용해야 할까요?〉 (한국어)
  • 〈애플리케이션 개발 측면에서 본 Drizzle ORM 대 Kysely 비교〉 (한국어) → 「アプリケーション開発の観点から見たDrizzle ORMとKyselyの比較」 (일본어)
  • 〈deno-task-hooks: Git 훅을 Deno 태스크로 쉽게 관리하기〉 (한국어) → deno-task-hooks: Easily Manage Git Hooks as Deno Tasks (영어)
  • Browser-Native Translation and Language Detection APIs Coming Soon (영어) → 〈브라우저 네이티브 번역 및 언어 감지 API 곧 출시 예정〉 (한국어)
Read more →
6

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

洪 民憙 (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!

Read more →
13
0
3

셸 언어는 때로 추하길 요구 받는다

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

이 글에서는 명령줄 인터페이스(CLI)를 지배하는 셸 언어의 독특한 설계 철학을 탐구하며, 셸 언어가 왜 때로는 "추함"을 받아들여야 하는지에 대한 이유를 설명합니다. Bash와 PowerShell을 비교하며, PowerShell이 가독성을 높이기 위해 장황해진 반면, Bash는 간결함을 유지하여 빠른 상호작용에 더 적합함을 지적합니다. 현대적인 셸인 Nushell이 이 균형을 맞추기 위해 노력하는 점을 언급하며, 셸 언어의 성공은 "아름다운 코드"와 "효율적인 상호작용" 사이의 균형에 달려 있음을 강조합니다. 마지막으로, 모든 도구는 사용 맥락에 맞게 설계되어야 한다는 더 넓은 소프트웨어 설계 원칙을 제시하며, 셸 언어의 맥락은 키보드와 사용자 사이의 빠른 대화임을 강조합니다. 이 글은 셸 언어 설계에 대한 흥미로운 통찰력을 제공하며, 소프트웨어 설계 시 맥락의 중요성을 일깨워 줍니다.

Read more →
0
0
0

deno-task-hooks: Git 훅을 Deno 태스크로 쉽게 관리하기

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

안녕하세요! 오늘은 제가 개발한 deno-task-hooks 패키지를 소개해 드리려고 합니다. 이 도구는 Deno 태스크를 Git 훅으로 사용할 수 있게 해주는 간단하면서도 유용한 패키지입니다.

어떤 문제를 해결하나요?

Git을 사용하는 개발 팀에서는 코드 품질 유지를 위해 커밋이나 푸시 전에 린트, 테스트 등의 검증 작업을 실행하는 것이 일반적입니다. 이러한 작업은 Git 훅을 통해 자동화할 수 있지만, 기존 방식에는 몇 가지 문제가 있었습니다:

  • Git 훅 스크립트를 팀원들과 공유하기 어려움 (.git 디렉토리는 보통 버전 관리에서 제외됨)
  • 각 개발자가 로컬에서 훅을 직접 설정해야 하는 번거로움
  • 훅 스크립트의 일관성 유지가 어려움

deno-task-hooks는 이러한 문제를 해결하기 위해 Deno의 태스크 러너를 활용합니다. Deno 태스크는 deno.json 파일에 정의되어 버전 관리가 가능하므로, 팀 전체가 동일한 Git 훅을 쉽게 공유할 수 있습니다.

어떻게 작동하나요?

deno-task-hooks의 작동 방식은 간단합니다:

  1. deno.json 파일에 Git 훅으로 사용할 Deno 태스크를 정의합니다.
  2. hooks:install 태스크를 실행하면, 정의된 태스크들이 자동으로 .git/hooks/ 디렉토리에 설치됩니다.
  3. 이후 Git 작업 시 해당 훅이 트리거되면 연결된 Deno 태스크가 실행됩니다.

설치 및 사용 방법

1. hooks:install 태스크 추가하기

먼저 deno.json 파일에 hooks:install 태스크를 추가합니다:

{
  "tasks": {
    "hooks:install": "deno run --allow-read=deno.json,.git/hooks/ --allow-write=.git/hooks/ jsr:@hongminhee/deno-task-hooks"
  }
}

2. Git 훅 정의하기

Git 훅은 hooks: 접두사 다음에 훅 이름(케밥 케이스)을 붙여 정의합니다. 예를 들어, pre-commit 훅을 정의하려면:

{
  "tasks": {
    "hooks:pre-commit": "deno check *.ts && deno lint"
  }
}

3. 훅 설치하기

다음 명령어를 실행하여 정의된 훅을 설치합니다:

deno task hooks:install

이제 Git 커밋을 실행할 때마다 pre-commit 훅이 자동으로 실행되어 TypeScript 파일을 검사하고 린트 검사를 수행합니다.

지원되는 Git 훅 종류

deno-task-hooks는 다음과 같은 모든 Git 훅 타입을 지원합니다:

  • applypatch-msg
  • commit-msg
  • fsmonitor-watchman
  • post-update
  • pre-applypatch
  • pre-commit
  • pre-merge-commit
  • pre-push
  • pre-rebase
  • pre-receive
  • prepare-commit-msg
  • push-to-checkout
  • sendemail-validate
  • update

이점

deno-task-hooks를 사용하면 다음과 같은 이점이 있습니다:

  1. 간편한 공유: Git 훅을 deno.json 파일에 정의하여 팀 전체가 동일한 훅을 사용할 수 있습니다.
  2. 설정 용이성: 새 팀원은 저장소를 클론한 후 한 번의 명령어로 모든 훅을 설치할 수 있습니다.
  3. 유지 관리 용이성: 훅 스크립트를 중앙에서 관리하므로 변경 사항을 쉽게 추적하고 적용할 수 있습니다.
  4. Deno의 안전성: Deno의 권한 모델을 활용하여 훅 스크립트의 보안을 강화할 수 있습니다.

마치며

deno-task-hooks는 작은 패키지이지만, Git과 Deno를 함께 사용하는 팀의 개발 경험을 크게 향상시킬 수 있습니다. 코드 품질 유지와 개발 워크플로우 자동화를 위해 한번 사용해 보세요!

패키지는 JSR에서 다운로드할 수 있으며, GitHub에서 소스 코드를 확인할 수 있습니다.

피드백과 기여는 언제나 환영합니다! 😊

Read more →
0

한국 소프트웨어 개발자들이 자주 틀리는 외래어 표기법

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

전에 단문으로 올렸던 글을 지속적으로 갱신해볼까 싶어 게시글로 만들어 봅니다.

영어 틀린 표기 올바른 표기
algorithm 알고리 알고리
app 어플
application 플리케이션 플리케이션
BASIC 베이 베이
directory 디렉 디렉
front-end 트엔드 트엔드
launch
license 라이 라이
message
method
parallel 페러 패럴
proxy
release 릴리 릴리
repository 레포 리파
shader 이더 이더
shell
Read more →
2
0
1

Browser-Native Translation and Language Detection APIs Coming Soon

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

Just reviewed the W3C draft for the Translator and Language Detector APIs. This is genuinely exciting development for web developers.

The proposal would add native browser support for:

  • Text translation between languages
  • Language detection of arbitrary text
  • Both with streaming capabilities

No more relying on third-party translation services or embedding external APIs for basic language operations. All processing happens locally in the browser.

The API design is clean and straightforward:

// Translation example
const translator = await Translator.create({
  sourceLanguage: "en",
  targetLanguage: "fr"
});

const translatedText = await translator.translate("Hello world");

// Language detection example
const detector = await LanguageDetector.create();
const results = await detector.detect("Hello world");
// Returns array of detected languages with confidence scores

This will be a game-changer for multilingual sites and applications. The browser handles downloading appropriate language models and manages usage quotas.

The spec is still in draft form but shows promising progress toward standardizing these capabilities across browsers. Looking forward to seeing this implemented.

Read more →
0

Bluesky는 X의 훌륭한 대안일 수 있지만, 연합우주의 대안은 아닙니다

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

최근 X(구 Twitter)를 떠나는 사용자들이 늘면서 Bluesky에 대한 관심이 높아지고 있습니다. Bluesky는 깔끔한 인터페이스와 과거 Twitter와 유사한 사용자 경험을 제공하며, '신뢰할 수 있는 이탈'이라는 매력적인 개념을 내세워 X의 유력한 대안으로 떠오르고 있습니다. 하지만 이 글에서는 Bluesky와 그 기반 프로토콜인 AT Protocol이 연합우주(fediverse)의 대안이 될 수 없는 이유를 설명합니다. Bluesky는 메시지 전달 방식 대신 공유 힙 방식을 사용하며, 이는 중앙 릴레이에 의존하게 만들어 탈중앙화의 이상과는 거리가 멀어집니다. 또한, 전역 뷰에 대한 집착은 차단 목록의 전체 공개와 같은 개인 정보 보호 문제를 야기하며, AT Protocol은 아직 특정 사기업에 의해 주도되고 있어 개방형 표준으로서의 한계를 가지고 있습니다. Bluesky는 이동 가능한 아이덴티티를 제공하지만, 여전히 중앙화된 요소에 의존하고 있으며, DM은 완전히 중앙화되어 있습니다. 결론적으로, Bluesky는 X의 훌륭한 대안이 될 수 있지만, 연합우주가 제공하는 탈중앙화된 가치와 경험을 대체하기는 어려울 것입니다. 이 글을 통해 Bluesky와 연합우주의 차이점을 명확히 이해하고, 자신에게 맞는 플랫폼을 선택하는 데 도움이 될 것입니다.

Read more →
5
0
3