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

洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social · 1042 following · 1714 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 메인테이너. , , , 等으로 自由 소프트웨어 만듦.

()

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

@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) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

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

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

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

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

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

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

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

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

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

Park Joon-Kyu's avatar
Park Joon-Kyu

@segfault87@hackers.pub

관점에 동의하고, 모델과 학습 데이터의 민주화를 어떻게 이룰 것인가에 대한 현실적인 부분을 좀 더 고민한다면 결국 국가나 국가들의 연합체이 주도하는 방향이 되지 않을까 싶다. 소프트웨어는 컴퓨터만 있으면 온전히 개인의 역량만으로 접근할 수 있지만 LLM은 특히 초대형 자본의 각축장이라 풀뿌리로 접근할 수 있는 길이 잘 보이지 않는 것 같다.



RE: https://hollo.social/@hongminhee/019bc575-4ab7-7812-a4f8-e293944a1db7

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

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

AI 企業(기업)이 F/OSS 코드로 LLM 訓練(훈련)하는 걸 막을 게 아니라, 訓練(훈련)한 모델을 公開(공개)하도록 要求(요구)해야 한다고 생각합니다.

撤收(철수)가 아니라 再專有(재전유)! GPL이 그랬던 것처럼요.

訓練(훈련) 카피레프트에 ()한 글을 썼습니다: 〈F/OSS 史唯(사유): 우리는 LLM을 拒否(거부)할 게 아니라 되찾아 와야 한다〉(한글).

Lobsters

@lobsters@mastodon.social

Histomat of F/OSS: We should reclaim LLMs, not reject them by @hongminhee lobste.rs/s/go7hr7
writings.hongminhee.org/2026/0

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

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

AI企業がF/OSSコードでLLMを訓練することを止めるのではなく、訓練したモデルを公開するよう要求すべきだと思います。

撤退ではなく、再専有。GPLがそうだったように。

訓練コピーレフトについて書きました:「F/OSSの唯物史観——LLMを拒絶するのではなく、取り戻すべきだ

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

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

AI 企業(기업)이 F/OSS 코드로 LLM 訓練(훈련)하는 걸 막을 게 아니라, 訓練(훈련)한 모델을 公開(공개)하도록 要求(요구)해야 한다고 생각합니다.

撤收(철수)가 아니라 再專有(재전유)! GPL이 그랬던 것처럼요.

訓練(훈련) 카피레프트에 ()한 글을 썼습니다: 〈F/OSS 史唯(사유): 우리는 LLM을 拒否(거부)할 게 아니라 되찾아 와야 한다〉(한글).

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

@hongminhee@hollo.social

Been thinking a lot about @algernon's recent post on FLOSS and LLM training. The frustration with AI companies is spot on, but I wonder if there's a different strategic path. Instead of withdrawal, what if this is our GPL moment for AI—a chance to evolve copyleft to cover training? Tried to work through the idea here: Histomat of F/OSS: We should reclaim LLMs, not reject them.

amos's avatar
amos

@fasterthanlime@hachyderm.io

I missed designing languages. ooc-lang.github.io/ was back in 2009!! 17 years ago! The language I'm making now is not a programming language but it's still really nice to design.

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

@hongminhee@hollo.social

LogTape 2.0.0 released!

LogTape is a zero-dependency logging library for JavaScript/TypeScript that works across Deno, Node.js, Bun, and browsers.

What's new in 2.0.0:

  • lazy() for dynamic context: with() now supports values that are evaluated at logging time, not when the logger is created. Child loggers inherit the lazy wrapper, so they always see the current value.
  • Configuration from files: New @logtape/config package lets you load logging configuration from JSON, YAML, or TOML instead of writing TypeScript code.
  • Better error logging: Pass Error objects directly to logger.error(err) instead of wrapping them in properties.
  • Async lazy evaluation: Logging methods now accept async callbacks for expensive async operations.
  • isEnabledFor() method: Check if a log level is enabled before running expensive computations.
  • Time-based log rotation: Rotate logs daily, hourly, or weekly with automatic cleanup of old files.
  • New integrations: Elysia framework support and log4js adaptor.

https://github.com/dahlia/logtape/discussions/133

Olivia Grace 🌸's avatar
Olivia Grace 🌸

@olivia@theforkiverse.com

I tried to look up "dinner" in Korean on papago but instead it told me I was looking up "discharged from the military" 👀

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

@hongminhee@hollo.social · Reply to Ian Wagner's post

@ianthetechie Glad to hear that the spacing matters to native speakers too! And thanks for suggesting “wall clock time”—it's a very clear alternative.

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

@hongminhee@hollo.social

I've been using this exact distinction for a while now. Since Korean, my native language, has distinct terms for the system (런타임), the point in time (實行時(실행 시)), and the duration (實行時間(실행 시간)), using a single spelling for all three in English always felt a bit blurry to me. This spelling convention helps bridge that gap and makes technical writing much more precise.

Personally, I find the meaning as indicated by Google's style guide the most clear, combined with an explicit case for hyphenation:

Runtime: use the system meaning. E.g. “the runtime was updated last week,” or “I'm using version 21 of the Java runtime.”

Run-time: use the moment meaning, but only when used in the adjective position. E.g. “run-time instrumentation is useful for finding bugs.”

Run time: use the duration meaning. E.g. “the run time was reduced by 5%,” or “a run time of five minutes is unacceptable.” In addition, when you want to use the moment meaning, but not as an adjective, this form should also be used. E.g. “typechecking happens at run time in our implementation.”

My Opinion on Run Time vs. Run-time vs. Runtime (by Bob Rubbens)

Lobsters

@lobsters@mastodon.social

My Opinion on Spelling Run Time vs. Run-time vs. Runtime lobste.rs/s/vnkwyb
bobrubbens.nl/post/my-opinion-

Christine Lemmer-Webber's avatar
Christine Lemmer-Webber

@cwebber@social.coop · Reply to Evan Prodromou's post

@evan *screaming in sharedInbox's design concession at the end of AP's standardization was already my biggest regret and this makes it much worse*

Evan Prodromou's avatar
Evan Prodromou

@evan@cosocial.ca · Reply to Evan Prodromou's post

You don't have to answer this if you don't know what it means.

The relevant section of the ActivityPub specification is here:

w3.org/TR/activitypub/#shared-

Evan Prodromou's avatar
Evan Prodromou

@evan@cosocial.ca

Should ActivityPub servers send public activities to all known `sharedInbox` endpoints on the network?

OptionVoters
Yes21 (18%)
Yes, but...24 (20%)
No, but...28 (23%)
No47 (39%)
洪 民憙 (Hong Minhee) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social · Reply to Dave bauer's post

@davebauerart Exactly. It's amazing how much code can teach us about people. Keep building those bridges!

Elena Rossini ⁂'s avatar
Elena Rossini ⁂

@_elena@mastodon.social

Oooooh my custom made sweatshirt has arrived at a pickup point! Going to retrieve it later today and I hope it turned out ok.

I intend to wear it at on the big day for the Social Web talks.

All fingers and toes crossed it looks good and fits well. Otherwise I have two other Fedi t-shirts as backups 😅

a photo showing the mockup for a sweatshirt - color: forest green - with the words "the future is federated" printed in white. The font is Eighties Comeback by independent type designer Nicky Laatz
ALT text detailsa photo showing the mockup for a sweatshirt - color: forest green - with the words "the future is federated" printed in white. The font is Eighties Comeback by independent type designer Nicky Laatz
洪 民憙 (Hong Minhee) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

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

@b00g13 Thank you! I strongly agree that code quality and maintainability will always be valuable, no matter how the industry evolves. I really appreciate your encouragement to stick to high standards.

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

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

@veroandi Thank you for recognizing that. It's comforting to know that our quiet contributions are seen and valued as a force for good. It makes me feel less alone in this.

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

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

@ged You're right. Finding that why really changes everything. It turns work into joy, and that energy keeps me going. Thank you for such a warm perspective!

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

@hongminhee@hollo.social · Reply to Anders Cornelius Madsen's post

@madsenandersc Thank you for sharing such a personal story. It truly warms my heart to hear that your son has found his tribe. I know how life-changing that feeling of belonging can be. I wish him all the best!

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

@hongminhee@hollo.social · Reply to Ramnarayan Kalyanaraman's post

@ramkay I am deeply moved by your words. Knowing that my work has earned the respect of users like you gives me the strength to keep contributing. Thank you.

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

@hongminhee@hollo.social · Reply to John O'Nolan's post

@john Thank you, John! Ghost's support has been a huge motivation for me to keep going. I'm proud that we're building the open web together.

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

@hongminhee@hollo.social

@albertmantingh Thank you. That means a lot to me.

티르's avatar
티르

@tirr@mitir.social

JPEG XL 이미지 포맷 지원이 Chromium에 머지되어서 지금 Canary 빌드에서 플래그를 켜면 사용해볼 수 있다.

구현체로 jxl-rs를 사용하는데 저도 상당부분 기여했습니다 많관부...
洪 民憙 (Hong Minhee) :nonbinary:'s avatar
洪 民憙 (Hong Minhee) :nonbinary:

@hongminhee@hollo.social

I've spent a long time asking myself why open source matters so much to me, why I keep coming back to it. I once joined a company purely because they promised I could do open source full-time (it didn't turn out well). Before that, I was doing open source inside and outside of regular jobs. And now, in the age of LLMs, when the value of code itself seems to be declining, I'm still here, still doing this.

Recently it clicked. I do open source because it's social work—in the sense that it lets me participate in society.

Everyone wants to belong to some community, to connect with others. But I was never good at the usual ways of doing that. Social activities that came naturally to others were difficult for me. In school, I had few friends. After class, I'd stay home assembling Lego or reading books alone. Then I discovered coding.

Coding was a wonderful hobby for me, especially because I encountered it at the dawn of the internet era. The first programming languages I properly learned were Perl, PHP, and JavaScript—all languages of the internet age. The synergy was something else.

Gradually I fell into the world of open source. And there, even someone like me—awkward at conventional social interaction—could be social. My code helped people. I could collaborate by exchanging code. I could have conversations, mediated by code. IRC, mailing lists, forums—these became my social media. Over time, “the group I wanted recognition from” became the people in the open source world. I didn't care much about being recognized by classmates, but I wanted to be recognized by these people I'd never met face to face.

That mindset still shapes me now, approaching forty. I still care more about recognition from open source programmers than from colleagues. The social activity that happens in open source communities is, after my family, the most important social activity in my life.

The specific things I build, the technical details—those matter less than I used to think. I just want to do the kind of social activity that suits me, and open source happens to be the way I do it.

That's all, really.

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

@hongminhee@hollo.social

Hongdown 0.2.0 is out! Hongdown is an opinionated formatter written in , and this release brings support, so you can now use it as a library in .js, , , and browsers.

New features:

  • Smart heading sentence case conversion with ~450 built-in proper nouns
  • SmartyPants-style typographic punctuation ("straight"“curly”)
  • External code formatter integration for code blocks
  • Directory argument support for batch formatting

Try it in the browser: https://dahlia.github.io/hongdown/

Release notes: https://github.com/dahlia/hongdown/discussions/10

Anthony Fu's avatar
Anthony Fu

@antfu.me@bsky.brid.gy

if people no longer read the code, API is no longer targeting humans, is there still a need for "good API design"? is it still worth the effort to figure out what would be the best for users, instead of the best of ai to understand/use? I don't know.

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

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

CLIツールを作ってると、「--repoで指定したリポジトリのブランチだけ補完候補に出したい」みたいな場面があるんですよね。でも普通のCLIパーサーだと各オプションが独立してて、これが意外と難しい。

TypeScript向け型安全CLIパーサーOptiqueで、この問題を解決する仕組みを作ったので、記事にまとめました。

https://zenn.dev/hongminhee/articles/aedde5d7fcc40e

Lobsters

@lobsters@mastodon.social

Your CLI's completion should know what options you've already typed by @hongminhee lobste.rs/s/5se1tq
hackers.pub/@hongminhee/2026/o

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

@hongminhee@hollo.social

When building CLI tools, shell completion usually treats each option in isolation. But sometimes valid values for one option depend on another—like branch names depending on which repository you're targeting.

Wrote about how I solved this in Optique, a type-safe CLI parser for TypeScript.

https://hackers.pub/@hongminhee/2026/optique-context-aware-cli-completion

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

@hongminhee@hackers.pub

Consider Git's -C option:

git -C /path/to/repo checkout <TAB>

When you hit Tab, Git completes branch names from /path/to/repo, not your current directory. The completion is context-aware—it depends on the value of another option.

Most CLI parsers can't do this. They treat each option in isolation, so completion for --branch has no way of knowing the --repo value. You end up with two unpleasant choices: either show completions for all possible branches across all repositories (useless), or give up on completion entirely for these options.

Optique 0.10.0 introduces a dependency system that solves this problem while preserving full type safety.

Static dependencies with or()

Optique already handles certain kinds of dependent options via the or() combinator:

import { flag, object, option, or, string } from "@optique/core";

const outputOptions = or(
  object({
    json: flag("--json"),
    pretty: flag("--pretty"),
  }),
  object({
    csv: flag("--csv"),
    delimiter: option("--delimiter", string()),
  }),
);

TypeScript knows that if json is true, you'll have a pretty field, and if csv is true, you'll have a delimiter field. The parser enforces this at runtime, and shell completion will suggest --pretty only when --json is present.

This works well when the valid combinations are known at definition time. But it can't handle cases where valid values depend on runtime input—like branch names that vary by repository.

Runtime dependencies

Common scenarios include:

  • A deployment CLI where --environment affects which services are available
  • A database tool where --connection affects which tables can be completed
  • A cloud CLI where --project affects which resources are shown

In each case, you can't know the valid values until you know what the user typed for the dependency option. Optique 0.10.0 introduces dependency() and derive() to handle exactly this.

The dependency system

The core idea is simple: mark one option as a dependency source, then create derived parsers that use its value.

import {
  choice,
  dependency,
  message,
  object,
  option,
  string,
} from "@optique/core";

function getRefsFromRepo(repoPath: string): string[] {
  // In real code, this would read from the Git repository
  return ["main", "develop", "feature/login"];
}

// Mark as a dependency source
const repoParser = dependency(string());

// Create a derived parser
const refParser = repoParser.derive({
  metavar: "REF",
  factory: (repoPath) => {
    const refs = getRefsFromRepo(repoPath);
    return choice(refs);
  },
  defaultValue: () => ".",
});

const parser = object({
  repo: option("--repo", repoParser, {
    description: message`Path to the repository`,
  }),
  ref: option("--ref", refParser, {
    description: message`Git reference`,
  }),
});

The factory function is where the dependency gets resolved. It receives the actual value the user provided for --repo and returns a parser that validates against refs from that specific repository.

Under the hood, Optique uses a three-phase parsing strategy:

  1. Parse all options in a first pass, collecting dependency values
  2. Call factory functions with the collected values to create concrete parsers
  3. Re-parse derived options using those dynamically created parsers

This means both validation and completion work correctly—if the user has already typed --repo /some/path, the --ref completion will show refs from that path.

Repository-aware completion with @optique/git

The @optique/git package provides async value parsers that read from Git repositories. Combined with the dependency system, you can build CLIs with repository-aware completion:

import {
  command,
  dependency,
  message,
  object,
  option,
  string,
} from "@optique/core";
import { gitBranch } from "@optique/git";

const repoParser = dependency(string());

const branchParser = repoParser.deriveAsync({
  metavar: "BRANCH",
  factory: (repoPath) => gitBranch({ dir: repoPath }),
  defaultValue: () => ".",
});

const checkout = command(
  "checkout",
  object({
    repo: option("--repo", repoParser, {
      description: message`Path to the repository`,
    }),
    branch: option("--branch", branchParser, {
      description: message`Branch to checkout`,
    }),
  }),
);

Now when you type my-cli checkout --repo /path/to/project --branch <TAB>, the completion will show branches from /path/to/project. The defaultValue of "." means that if --repo isn't specified, it falls back to the current directory.

Multiple dependencies

Sometimes a parser needs values from multiple options. The deriveFrom() function handles this:

import {
  choice,
  dependency,
  deriveFrom,
  message,
  object,
  option,
} from "@optique/core";

function getAvailableServices(env: string, region: string): string[] {
  return [`${env}-api-${region}`, `${env}-web-${region}`];
}

const envParser = dependency(choice(["dev", "staging", "prod"] as const));
const regionParser = dependency(choice(["us-east", "eu-west"] as const));

const serviceParser = deriveFrom({
  dependencies: [envParser, regionParser] as const,
  metavar: "SERVICE",
  factory: (env, region) => {
    const services = getAvailableServices(env, region);
    return choice(services);
  },
  defaultValues: () => ["dev", "us-east"] as const,
});

const parser = object({
  env: option("--env", envParser, {
    description: message`Deployment environment`,
  }),
  region: option("--region", regionParser, {
    description: message`Cloud region`,
  }),
  service: option("--service", serviceParser, {
    description: message`Service to deploy`,
  }),
});

The factory receives values in the same order as the dependency array. If some dependencies aren't provided, Optique uses the defaultValues.

Async support

Real-world dependency resolution often involves I/O—reading from Git repositories, querying APIs, accessing databases. Optique provides async variants for these cases:

import { dependency, string } from "@optique/core";
import { gitBranch } from "@optique/git";

const repoParser = dependency(string());

const branchParser = repoParser.deriveAsync({
  metavar: "BRANCH",
  factory: (repoPath) => gitBranch({ dir: repoPath }),
  defaultValue: () => ".",
});

The @optique/git package uses isomorphic-git under the hood, so gitBranch(), gitTag(), and gitRef() all work in both Node.js and Deno.

There's also deriveSync() for when you need to be explicit about synchronous behavior, and deriveFromAsync() for multiple async dependencies.

Wrapping up

The dependency system lets you build CLIs where options are aware of each other—not just for validation, but for shell completion too. You get type safety throughout: TypeScript knows the relationship between your dependency sources and derived parsers, and invalid combinations are caught at compile time.

This is particularly useful for tools that interact with external systems where the set of valid values isn't known until runtime. Git repositories, cloud providers, databases, container registries—anywhere the completion choices depend on context the user has already provided.

This feature will be available in Optique 0.10.0. To try the pre-release:

deno add jsr:@optique/core@0.10.0-dev.311

Or with npm:

npm install @optique/core@0.10.0-dev.311

See the documentation for more details.

← Newer
Older →