洪 民憙 (Hong Minhee)'s avatar

洪 民憙 (Hong Minhee)

@hongminhee@hollo.social · 948 following · 1260 followers

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

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

()

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

@hongminhee@hollo.social

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@hongminhee@hollo.social · Reply to Froyo's post

@chocologic 혹시 계정 필요하면 언제라도 말씀 주세요 ㅎㅎㅎ

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

@hongminhee@hollo.social · Reply to Froyo's post

@chocologic Hackers' Pub은 어떠세요?!

silverpill's avatar
silverpill

@silverpill@mitra.social · Reply to Emelia 👸🏻's post

@thisismissem @partizan Those posts are addressed to special subscribers collection, and yes, every subscriber is also mentioned for compatibility.

Emelia 👸🏻's avatar
Emelia 👸🏻

@thisismissem@hachyderm.io · Reply to silverpill's post

@silverpill @partizan how do you make subscriber only posts work, when you generally only have followers as a collection to address to? (Unless you use direct addressing to every subscriber via like bto/bcc?)

silverpill's avatar
silverpill

@silverpill@mitra.social · Reply to Emelia 👸🏻's post

@thisismissem @partizan

Mitra has pay to subscribe feature, there are subscribers-only posts (compatible with other fedi services) and an integrated payment processor. It uses Monero, so transactions are private and can't be blocked by 3rd parties.

But legal compliance might not be possible in some countries. For example, EU plans to ban Monero by 2027.

이찬행's avatar
이찬행

@2chanhaeng@hackers.pub

거의 반은 내가 기여했네 ㅎㅎ

  • @fedify/fedify: Custom Collection 디스패처 setter
  • @fedify/cli: webfinger 커맨드
  • @fedify/next: 패키지 제작 및 예제 생성
  • @fedify/sveltekit: 패키지 분리, 타입 수정 및 예제 생성
  • @fedify/cfworkers, @fedify/denokv, @fedify/hono: 패키지 분리



RE: https://hollo.social/@fedify/0198fe69-a7be-7c09-bc42-81e27fc1dd3c

Fedify: ActivityPub server framework's avatar
Fedify: ActivityPub server framework

@fedify@hollo.social

The monorepo has grown to 16 packages!

We've been working hard to make Fedify more modular and easier to integrate with your favorite tools and platforms. From the core framework to database drivers, from CLI tools to web framework integrations—we've got you covered.

Our packages now include:

  • Core framework and CLI tools
  • Web framework integrations: Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit
  • Database drivers: PostgreSQL, Redis, SQLite, AMQP/RabbitMQ
  • Platform integrations: Cloudflare Workers, Deno KV
  • Testing utilities

Each package is available on JSR and/or npm, making it easy to pick exactly what you need for your ActivityPub implementation.

What integration would you like to see next? Let us know!

A table showing 16 Fedify packages with three columns: Package name, registry availability (JSR and npm links), and Description. The packages include the core @fedify/fedify framework, CLI toolchain, database drivers (PostgreSQL, Redis, SQLite, AMQP/RabbitMQ), web framework integrations (Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit, Cloudflare Workers), Deno KV integration, and testing utilities. Most packages are available on both JSR and npm registries, with some exceptions like @fedify/denokv (JSR only) and @fedify/elysia, @fedify/nestjs, @fedify/next (npm only).
ALT text detailsA table showing 16 Fedify packages with three columns: Package name, registry availability (JSR and npm links), and Description. The packages include the core @fedify/fedify framework, CLI toolchain, database drivers (PostgreSQL, Redis, SQLite, AMQP/RabbitMQ), web framework integrations (Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit, Cloudflare Workers), Deno KV integration, and testing utilities. Most packages are available on both JSR and npm registries, with some exceptions like @fedify/denokv (JSR only) and @fedify/elysia, @fedify/nestjs, @fedify/next (npm only).
Fedify: ActivityPub server framework's avatar
Fedify: ActivityPub server framework

@fedify@hollo.social

The monorepo has grown to 16 packages!

We've been working hard to make Fedify more modular and easier to integrate with your favorite tools and platforms. From the core framework to database drivers, from CLI tools to web framework integrations—we've got you covered.

Our packages now include:

  • Core framework and CLI tools
  • Web framework integrations: Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit
  • Database drivers: PostgreSQL, Redis, SQLite, AMQP/RabbitMQ
  • Platform integrations: Cloudflare Workers, Deno KV
  • Testing utilities

Each package is available on JSR and/or npm, making it easy to pick exactly what you need for your ActivityPub implementation.

What integration would you like to see next? Let us know!

A table showing 16 Fedify packages with three columns: Package name, registry availability (JSR and npm links), and Description. The packages include the core @fedify/fedify framework, CLI toolchain, database drivers (PostgreSQL, Redis, SQLite, AMQP/RabbitMQ), web framework integrations (Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit, Cloudflare Workers), Deno KV integration, and testing utilities. Most packages are available on both JSR and npm registries, with some exceptions like @fedify/denokv (JSR only) and @fedify/elysia, @fedify/nestjs, @fedify/next (npm only).
ALT text detailsA table showing 16 Fedify packages with three columns: Package name, registry availability (JSR and npm links), and Description. The packages include the core @fedify/fedify framework, CLI toolchain, database drivers (PostgreSQL, Redis, SQLite, AMQP/RabbitMQ), web framework integrations (Express, Hono, H3, Elysia, NestJS, Next.js, SvelteKit, Cloudflare Workers), Deno KV integration, and testing utilities. Most packages are available on both JSR and npm registries, with some exceptions like @fedify/denokv (JSR only) and @fedify/elysia, @fedify/nestjs, @fedify/next (npm only).
tesaguri 🦀🦝's avatar
tesaguri 🦀🦝

@tesaguri@fedibird.com

ActivityPubの仕様の策定、RDFトリビアの議論で無限に時間を食っている感はある

이찬행's avatar
이찬행

@2chanhaeng@hackers.pub · Reply to Jaeyeol Lee's post

@kodingwarrior 요런 디자인은 어떠세용

Hackers' public
1st MEETUP @SEOUL

2025. 09. 14. 15:00 KST
서울특별시 성동구 상원길 26 튜링의 사과

Code As A Canvas : 코드에서 예술작품이 되기까지 (@jakeseo@hackers.pub)

폰트는 어떻게 만들어지는가 - NeoDGM 사례로 살펴보는 개발 후일담 (@dalgona@hackers.pub)
ALT text detailsHackers' public 1st MEETUP @SEOUL 2025. 09. 14. 15:00 KST 서울특별시 성동구 상원길 26 튜링의 사과 Code As A Canvas : 코드에서 예술작품이 되기까지 (@jakeseo@hackers.pub) 폰트는 어떻게 만들어지는가 - NeoDGM 사례로 살펴보는 개발 후일담 (@dalgona@hackers.pub)
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

Are there any active chat rooms on or where developers gather?

Chee Aun 🤔's avatar
Chee Aun 🤔

@cheeaun@mastodon.social

This has been in the to-do list for years.

Yes, custom emojis work inside CW and poll fields :blobpeek:

Demo of inserting custom emojis inside content warning fields and poll fields in the composer on Phanpy.
ALT text detailsDemo of inserting custom emojis inside content warning fields and poll fields in the composer on Phanpy.
洪 民憙 (Hong Minhee)'s avatar
洪 民憙 (Hong Minhee)

@hongminhee@hollo.social

0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.

https://hackers.pub/@hongminhee/2025/optique-030

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

@hongminhee@hackers.pub


We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.

What's new

  • Required Boolean flags with the new flag() parser for dependent options patterns
  • Flexible type defaults in withDefault() supporting union types for conditional CLI structures
  • Extended or() capacity now supporting up to 10 parsers (previously 5)
  • Enhanced merge() combinator that works with any object-producing parser, not just object()
  • Context-aware help using the new longestMatch() combinator
  • Version display support in both @optique/core and @optique/run
  • Structured output functions for consistent terminal formatting

Required Boolean flags with flag()

The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.

Consider a scenario where certain options only make sense when a mode is explicitly enabled:

import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";

// Without the --advanced flag, these options aren't available
const parser = withDefault(
  object({
    advanced: flag("--advanced"),
    maxThreads: option("--threads", integer()),
    cacheSize: option("--cache-size", integer())
  }),
  { advanced: false as const }
);

// Usage:
// myapp                    → { advanced: false }
// myapp --advanced         → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success

This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.

Union types in withDefault()

Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:

const conditionalParser = withDefault(
  object({
    server: flag("-s", "--server"),
    port: option("-p", "--port", integer()),
    host: option("-h", "--host", string())
  }),
  { server: false as const }
);

// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }

This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.

More flexible merge() combinator

The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:

const transformedConfig = map(
  object({
    host: option("--host", string()),
    port: option("--port", integer())
  }),
  ({ host, port }) => ({ endpoint: `${host}:${port}` })
);

const conditionalFeatures = withDefault(
  object({
    experimental: flag("--experimental"),
    debugLevel: option("--debug-level", integer())
  }),
  { experimental: false as const }
);

// Can now merge different parser types
const appConfig = merge(
  transformedConfig,        // map() result
  conditionalFeatures,      // withDefault() parser
  object({                  // traditional object()
    verbose: option("-v", "--verbose")
  })
);

This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.

Context-aware help with longestMatch()

The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:

const normalParser = object({
  help: constant(false),
  command: or(
    command("list", listOptions),
    command("add", addOptions)
  )
});

const contextualHelp = object({
  help: constant(true),
  commands: multiple(argument(string())),
  helpFlag: flag("--help")
});

const cli = longestMatch(normalParser, contextualHelp);

// myapp --help           → Shows global help
// myapp list --help      → Shows help for 'list' command
// myapp add --help       → Shows help for 'add' command

The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.

Version display support

Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:

// @optique/run - simple API
run(parser, {
  version: "1.0.0",  // Adds --version flag
  help: "both"
});

// @optique/core/facade - detailed control
run(parser, "myapp", args, {
  version: {
    mode: "both",     // --version flag AND version command
    value: "1.0.0",
    onShow: process.exit
  }
});

The API follows the same pattern as help configuration, keeping things consistent and predictable.

Structured output functions

The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:

import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";

// Standard output with automatic formatting
print(message`Processing ${filename}...`);

// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });

// Custom printer for specific needs
const debugPrint = createPrinter({
  stream: "stderr",
  colors: true,
  maxWidth: 80
});

debugPrint(message`Debug: ${details}`);

These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.

Breaking changes

While we've tried to maintain backward compatibility, there are a few changes to be aware of:

  • The help option in @optique/run no longer accepts "none". Simply omit the option to disable help.
  • Custom parsers implementing getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.
  • The object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.

Upgrading to 0.3.0

To upgrade to Optique 0.3.0, update both packages:

# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0

# npm
npm update @optique/core @optique/run

# pnpm
pnpm update @optique/core @optique/run

# Yarn
yarn upgrade @optique/core @optique/run

# Bun
bun update @optique/core @optique/run

If you're only using the core package:

# Deno (JSR)
deno add @optique/core@^0.3.0

# npm
npm update @optique/core

Looking ahead

These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.

As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.

Anuj Ahooja's avatar
Anuj Ahooja

@quillmatiq@mastodon.social

Bluesky is kicking off the formal process to make ATProto an IETF standard 👀

datatracker.ietf.org/doc/bofre

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

@hongminhee@hollo.social

0.3.0 is out with dependent options and flexible parser composition, shaped by feedback from @z9mb1's work migrating @fedify CLI from Cliffy to Optique.

https://hackers.pub/@hongminhee/2025/optique-030

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

@hongminhee@hackers.pub


We're releasing Optique 0.3.0 with several improvements that make building complex CLI applications more straightforward. This release focuses on expanding parser flexibility and improving the help system based on feedback from the community, particularly from the Fedify project's migration from Cliffy to Optique. Special thanks to @z9mb1 for her invaluable insights during this process.

What's new

  • Required Boolean flags with the new flag() parser for dependent options patterns
  • Flexible type defaults in withDefault() supporting union types for conditional CLI structures
  • Extended or() capacity now supporting up to 10 parsers (previously 5)
  • Enhanced merge() combinator that works with any object-producing parser, not just object()
  • Context-aware help using the new longestMatch() combinator
  • Version display support in both @optique/core and @optique/run
  • Structured output functions for consistent terminal formatting

Required Boolean flags with flag()

The new flag() parser creates Boolean flags that must be explicitly provided. While option() defaults to false when absent, flag() fails parsing entirely if not present. This subtle difference enables cleaner patterns for dependent options.

Consider a scenario where certain options only make sense when a mode is explicitly enabled:

import { flag, object, option, withDefault } from "@optique/core/parser";
import { integer } from "@optique/core/valueparser";

// Without the --advanced flag, these options aren't available
const parser = withDefault(
  object({
    advanced: flag("--advanced"),
    maxThreads: option("--threads", integer()),
    cacheSize: option("--cache-size", integer())
  }),
  { advanced: false as const }
);

// Usage:
// myapp                    → { advanced: false }
// myapp --advanced         → Error: --threads and --cache-size required
// myapp --advanced --threads 4 --cache-size 100 → Success

This pattern is particularly useful for confirmation flags (--yes-i-am-sure) or mode switches that fundamentally change how your CLI behaves.

Union types in withDefault()

Previously, withDefault() required the default value to match the parser's type exactly. Now it supports different types, creating union types that enable conditional CLI structures:

const conditionalParser = withDefault(
  object({
    server: flag("-s", "--server"),
    port: option("-p", "--port", integer()),
    host: option("-h", "--host", string())
  }),
  { server: false as const }
);

// Result type is now a union:
// | { server: false }
// | { server: true, port: number, host: string }

This change makes it much easier to build CLIs where different flags enable different sets of options, without resorting to complex or() chains.

More flexible merge() combinator

The merge() combinator now accepts any parser that produces object-like values. Previously limited to object() parsers, it now works with withDefault(), map(), and other transformative parsers:

const transformedConfig = map(
  object({
    host: option("--host", string()),
    port: option("--port", integer())
  }),
  ({ host, port }) => ({ endpoint: `${host}:${port}` })
);

const conditionalFeatures = withDefault(
  object({
    experimental: flag("--experimental"),
    debugLevel: option("--debug-level", integer())
  }),
  { experimental: false as const }
);

// Can now merge different parser types
const appConfig = merge(
  transformedConfig,        // map() result
  conditionalFeatures,      // withDefault() parser
  object({                  // traditional object()
    verbose: option("-v", "--verbose")
  })
);

This improvement came from recognizing that many parsers ultimately produce objects, and artificially restricting merge() to only object() parsers was limiting composition patterns.

Context-aware help with longestMatch()

The new longestMatch() combinator selects the parser that consumes the most input tokens. This enables sophisticated help systems where command --help shows help for that specific command rather than global help:

const normalParser = object({
  help: constant(false),
  command: or(
    command("list", listOptions),
    command("add", addOptions)
  )
});

const contextualHelp = object({
  help: constant(true),
  commands: multiple(argument(string())),
  helpFlag: flag("--help")
});

const cli = longestMatch(normalParser, contextualHelp);

// myapp --help           → Shows global help
// myapp list --help      → Shows help for 'list' command
// myapp add --help       → Shows help for 'add' command

The run() functions in both @optique/core/facade and @optique/run now use this pattern automatically, so your CLI gets context-aware help without any additional configuration.

Version display support

Both @optique/core/facade and @optique/run now support version display through --version flags and version commands. See the runners documentation for details:

// @optique/run - simple API
run(parser, {
  version: "1.0.0",  // Adds --version flag
  help: "both"
});

// @optique/core/facade - detailed control
run(parser, "myapp", args, {
  version: {
    mode: "both",     // --version flag AND version command
    value: "1.0.0",
    onShow: process.exit
  }
});

The API follows the same pattern as help configuration, keeping things consistent and predictable.

Structured output functions

The new output functions in @optique/run provide consistent terminal formatting with automatic capability detection. Learn more in the messages documentation:

import { print, printError, createPrinter } from "@optique/run";
import { message } from "@optique/core/message";

// Standard output with automatic formatting
print(message`Processing ${filename}...`);

// Error output to stderr with optional exit
printError(message`File ${filename} not found`, { exitCode: 1 });

// Custom printer for specific needs
const debugPrint = createPrinter({
  stream: "stderr",
  colors: true,
  maxWidth: 80
});

debugPrint(message`Debug: ${details}`);

These functions automatically detect terminal capabilities and apply appropriate formatting, making your CLI output consistent across different environments.

Breaking changes

While we've tried to maintain backward compatibility, there are a few changes to be aware of:

  • The help option in @optique/run no longer accepts "none". Simply omit the option to disable help.
  • Custom parsers implementing getDocFragments() need to update their signature to use DocState<TState> instead of direct state values.
  • The object() parser now uses greedy parsing, attempting to consume all matching fields in one pass. This shouldn't affect most use cases but may change parsing order in complex scenarios.

Upgrading to 0.3.0

To upgrade to Optique 0.3.0, update both packages:

# Deno (JSR)
deno add @optique/core@^0.3.0 @optique/run@^0.3.0

# npm
npm update @optique/core @optique/run

# pnpm
pnpm update @optique/core @optique/run

# Yarn
yarn upgrade @optique/core @optique/run

# Bun
bun update @optique/core @optique/run

If you're only using the core package:

# Deno (JSR)
deno add @optique/core@^0.3.0

# npm
npm update @optique/core

Looking ahead

These improvements came from real-world usage and community feedback. We're particularly interested in hearing how the new dependent options patterns work for your use cases, and whether the context-aware help system meets your needs.

As always, you can find complete documentation at optique.dev and file issues or suggestions on GitHub.

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

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

@thisismissem It turned out the server process wasn't dying; the issue was that it couldn't respond due to high load. I do have logs. I think I need to separate the background job worker from the web application server.

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

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

DevOps is really too difficult…

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

@hongminhee@hollo.social

Recently, whenever I write or share (boost) a post on my hollo.social instance, the server consistently crashes immediately afterward, and I haven't found the cause yet. Since I haven't received similar reports from other Hollo users, I suspect it might be an issue with my deployment configuration…

Jaeyeol Lee's avatar
Jaeyeol Lee

@kodingwarrior@hackers.pub

Hi there 👋

I’m Jaeyeol, a software engineer who loves Neovim and Zed. I see myself as a pragmatic builder — I focus on creating software that I’d genuinely want to use, made for people rather than for technology’s sake. Beyond coding, I also experiment actively within developer communities, often initiating gatherings and exploring new ways for people to connect and share.

One of my current personal projects happens to be in the fediverse space: cosmoslide — a slideshare-like service for sharing presentations across the fediverse. It’s built with NestJS (@fedify/nestjs) for the backend, Next.js for the frontend (most of it is vibe-coded, so I’ll probably rewrite it later 😅)

The project is still in development, with an expected first release between late September and mid October. It started as a personal experiment, but I’m excited to see how it may contribute to the broader fediverse ecosystem.

I enjoy meeting others who are curious about new ideas, whether in technology or in community.

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

@hongminhee@hollo.social · Reply to PolyWolf's post

@PolyWolf Thanks!

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

@hongminhee@hollo.social

I'm currently improving Optique's façade API, and in the process, I've added a few more parsers. Optique 0.3.0 might have some breaking changes on the API side. Of course, these changes will be thoroughly documented in the changelog.

just small circles 🕊's avatar
just small circles 🕊

@smallcircles@social.coop · Reply to BotKit by Fedify :botkit:'s post

@botkit @gaebalgom

The development of fedify and botkit are handled splendidly, and all the progress is delightful to behold. Thanks to everyone involved for all the great work you do!

BotKit by Fedify :botkit:'s avatar
BotKit by Fedify :botkit:

@botkit@hollo.social

We're excited to announce the release of BotKit 0.3.0! This release marks a significant milestone as now supports .js alongside , making it accessible to a wider audience. The minimum required Node.js version is 22.0.0. This dual-runtime support means you can now choose your preferred runtime while building with the same powerful BotKit APIs.

One of the most requested features has landed: poll support! You can now create interactive polls in your messages, allowing followers to vote on questions with single or multiple-choice options. Polls are represented as ActivityPub Question objects with proper expiration times, and your bot can react to votes through the new onVote event handler. This feature enhances engagement possibilities and brings BotKit to feature parity with major platforms like Mastodon and Misskey.

// Create a poll with multiple choices
await session.publish(text`What's your favorite programming language?`, {
  class: Question,
  poll: {
    multiple: true,  // Allow multiple selections
    options: ["JavaScript", "TypeScript", "Python", "Rust"],
    endTime: Temporal.Now.instant().add({ hours: 24 }),
  },
});

// Handle votes
bot.onVote = async (session, vote) => {
  console.log(`${vote.actor} voted for "${vote.option}"`);
};

The web frontend has been enhanced with a new followers page, thanks to the contribution from Hyeonseo Kim (@gaebalgom)! The /followers route now displays a paginated list of your bot's followers, and the follower count on the main profile page is now clickable, providing better visibility into your bot's audience. This improvement makes the web interface more complete and user-friendly.

For developers looking for alternative storage backends, we've introduced the SqliteRepository through the new @fedify/botkit-sqlite package. This provides a production-ready SQLite-based storage solution with ACID compliance, write-ahead logging (WAL) for optimal performance, and proper indexing. Additionally, the new @fedify/botkit/repository module offers MemoryCachedRepository for adding an in-memory cache layer on top of any repository implementation, improving read performance for frequently accessed data.

This release also includes an important security update: we've upgraded to 1.8.8, ensuring your bots stay secure and compatible with the latest ActivityPub standards. The repository pattern has been expanded with new interfaces and types like RepositoryGetMessagesOptions, RepositoryGetFollowersOptions, and proper support for polls storage through the KvStoreRepositoryPrefixes.polls option, providing more flexibility for custom implementations.

McK's avatar
McK

@mck@hackers.pub

직원용 ETL CLI 인터페이스를 대충 해뒀는데 조만간 optique로 정리해야겠다

튜링의 사과's avatar
튜링의 사과

@TuringAppleDev@mastodon.social

안녕하세요. 성수동에 위치한 개발자 아지트 공간 튜링의사과입니다.

Mastodon 이전에 가입 후 관리를 못했지만 앞으로는 꾸준한 활동으로 인사 드리겠습니다. 잘 부탁드립니다. 😀

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

@hongminhee@hackers.pub

SocialHub와 분산 네트워크의 기반〉 (@fediversereport)

jnkrtech's avatar
jnkrtech

@jnkrtech@treehouse.systems · Reply to 洪 民憙 (Hong Minhee)'s post

@hongminhee I love what you’ve done with this library! I’m already pushing my coworkers to try to get them to adopt it. I’ve been frustrated by yargs for years.

silverpill's avatar
silverpill

@silverpill@mitra.social · Reply to Emelia 👸🏻's post

@thisismissem

>moving on AT Proto is much simpler for users than moving on ActivityPub, where you don't get to keep your posts currently

Can keep the posts too if your server implements FEP-ef61.

>So if the government decides to crack down on your fediverse instance, you're more screwed than if they crack down on Bluesky PBC or bluesky.social

In the worst case you just sign up on another Fediverse instance. The rest of the network is still there and all connections can be restored.

If Bluesky shuts down, it's over. They control almost all infrastructure. They control did:plc which is centralized. They have developers, moderators, and they have funding. Once all of that disappears, only a tiny group of power users remains who will burn out in a month.

@stinerman @Gargron @tom

silverpill's avatar
silverpill

@silverpill@mitra.social

Good thread: https://mastodon.social/@Gargron/115074631868019158

@Gargron was dogpiled by Bluesky shills but he's right about everything.

Imagine claiming that you're a decentralized network and then adding a geoblock because of some draconian "law" in some random country.

Connected Places's avatar
Connected Places

@fediversereport@mastodon.social

New: SocialHub and the Substrate of Decentralised Networks

SocialHub, one of the primary forums to talk about the and , has been struggling how to continue the operation. Decentralised networks need a coordination layer, but how to build this in a decentralised manner?

connectedplaces.online/socialh

julian's avatar
julian

@julian@community.nodebb.org · Reply to Connected Places's post

The issue of SocialHub is an interesting one, because where we are today is an odd situation where you have activitypub developers fragmented across multiple collaborative channels.

  • Some discuss their issues on their respective repositories only
  • Some discuss on SocialHub
  • Some discuss on Matrix channels

However, the bottom line truth is as follows: every ActivityPub developer is on the fediverse, ergo why shouldn't ActivityPub-focused discussions take place on the fediverse as well?

Up until this year, SocialHub has been an island separate from the fediverse. I used this analogy in my talk at fedicon to describe how lonely starting a community can be.

To SocialHub's credit, they have created a community of ActivityPub developers that exists to this day, kudos to them! The question remains now whether SocialHub performing their function adequately — to bring together ActivityPub developers of all stripes.

That's a question worth exploring in and of itself.

cc @fediversereport@mastodon.social @julian@activitypub.space

← Newer
Older →