2026年 01月 20日

このサイト開くと、便利情報として右上に IPv4 か IPv6 か表示するようにしてあるんだけど、ここに今繋いでる HTTP のバージョンも出すようにしてみた。

/.ip としてシンプルな情報を出すエンドポイントが前からあるのでここに足しただけ

      "/.ip":
        header.set: "Cache-Control: private"
        mruby.handler: |
          lambda do |env|
            ipv6 = env["REMOTE_ADDR"].include?(":")
            proto = {"HTTP/2" => "h2", "HTTP/3" => "h3"}.fetch(env["SERVER_PROTOCOL"], "")
            [200, {'content-type' => 'text/plain'}, [ "#{ipv6 ? 'IPv6' : 'IPv4'} #{proto}" ]]
          end


直接関係ないけど、h2o からproxy.reverse.url のバックエンドに直接こういう変数を入れたカスタムしたヘッダを送るのはあんまり簡単でなさそう? http_request 使うぐらい?

2026年 01月 19日

Chromeの開発者ツールでそのプロトコルが選ばれた理由が表示されることに気付いた

ずーーーと「なんでh3にならんのかな〜」ということをやってたけど、この開発者ツールのプロトコルの部分にホバーすると理由が出るということにようやく気付いた。ただ出ないこともある

HTTPS レコードで勝った場合

`Chrome` used a `HTTP/3` connection due to the `DNS record` indicating `HTTP/3` support, which won a race against establishing a connection using a different `HTTP` version.

HTTPSレコードちゃんと使ってるんだ。

ただレースして勝ったから使っていると書いてある。従来のTCP接続も同時に投げているようだ。

おそらく初回接続だけで、次からは Alt-Svc 優先になると思われる。(シークレットウィンドウで初回アクセスするとこれになる)

Alt-Svc ヘッダで勝った場合

`Chrome` used a `HTTP/3` connection induced by an `Alt-Svc` header without racing against establishing a connection using a different `HTTP` version.

2回目移行の接続。こっちはレースしないで決め打ちで繋ぐんだなあ

microdata → JSON-LD

JSON-LD 全然好きではないが、microdata でやっていた構造情報を JSON-LD にのせかえてみた。どっちにしろたいした情報はないが……

microdata だと display: none な要素を使ってメタデータを埋めこむ必要がしばしばあって、これがDOMの構成要素を増やす要因になっていて気になっていた。

最近のChromeだとDOM要素多いとパフォーマンス悪化するぞと脅してくる。

HTMLとしては非常にシンプルになって気持ちよいけど、やっぱ microdata のほうが好きだなあ。

2026年 01月 15日

プログラミング言語はコミュニケーションのためのもの

プログラムろくに書いたことない人はプログラミング言語をシンプルに誤解してて、機械語と区別がついていないのだろうと思う。

プログラミング言語はたまたま最終的に機械語に変換できるだけで、前提は人間同士(自分自身も含む)のコミュニケーション言語であるという理解がない。形式言語という形で意図を正確に伝えられるのがプログラミング言語であって、そうすると、たまたま実行可能にしやすいだけ。

LLMとのやりとりで自然言語を通してプログラミング言語を生成するのは、この意図が正確に反映されたかを、人間がレビューする必要性があるからにほかならない。

「ちゃんとできたか」を確認するのは最終的にはその要求をしている主体だ。一方で、人間は要求・要件を自然言語で100%正確に漏れなく曖昧さなく書くことはできない。なぜなら自然言語だから。やってみたいことを書き出したら矛盾することすらある。

プログラミングは、単なる実装(機械への命令)というより、曖昧な人間の意思を形式言語でミラーして具現化する行為であって、これはLLMを通じても別に変わらない。

とにかく本質的には人間は自分が欲しいものを、そもそも自分自身でも正確に理解していないところにある。「AIが無限に強くなれば言わなくてもやってくれる」という期待は、自分は自分が欲しいものを正確に理解しているという根本的な錯覚に基いている。

たとえAIが神になっても祈る側の願いが不定な以上は結果は定まらず、祈りと矛盾しないが意図しない結果がうまれる。この手の話は神話の時代からあり、上記の通り人間の埋め込みバグなので解決することはない。

だからこそ一度コードという形で曖昧さをなくし、テスト可能にし、自分の要求がなんだったのかを矛盾なく記述する必要がある。

ScreenToGif

Windows でスクリーンレコード気軽にとるなら ScreenToGif がよさげ?

名前からしてデフォルトでは anigif 出力なんだけど、WebM 形式にするとエンコーダーを選べて、普通に AVIF 出力ができる。録音はそもそもされないみたい。


macOS だと何がいいんだろうな。Kap なのかな。今度使ってみる。

Kap 非常にシンプルで完璧に使い勝手良いが、デフォルトだと WebM + VP9 か AV1 + MP4 で、パラメータもカスタマイズできないみたい。まぁ VP9 でもいいけども

簡易並列テストランナー

複数のテストを並列実行でマルチカラムで表示するやつをGeminiに書いてもらった。依存なし180行 で必要十分なのができて満足

人間が実行したときは途中経過を見せて、tty 経由じゃなければ最後まで無言でやってFAILしたものだけログをすべて出すというようにしてる。(Agentが実行するとき対策)

もうちょっと綺麗に(108行)なった。

2026年 01月 14日

日記の編集画面に過去エントリのリンクを一瞬で貼れる機能をつけた

よく昔の日記を検索してはりつけるので、さっさと作ればよかった。

Ctrl-L で起動して、Ctrl-N Ctrl-P で選択し、よくわからんかったら一度 Shift-Enter で別窓で開き、確定なら Enter で挿入できる。すごい便利

スクリーンレコードしたやつを video 要素でそのまま貼る。

↑試しにスクリーンレコード(OBS)したものを以下のように AV1 + webm にして、video 要素ではりつけてみた。107KB

こういうことするとき anigif を貼り付けてたけど、もうそんなことしないでもよい時代になっているか?

$ ffmpeg -ss 1 -i "/Users/cho45/Movies/2026-01-14 23-01-13.mkv" \
	-t "5" \
	-vf "scale=1280:-2" \
	-an \
	-c:v libsvtav1 \
	-crf 45 \
	-preset 4 \
	-svtav1-params tune=0:enable-overlays=1 \
	-pix_fmt yuv420p \
	"./foo.webm"
2026年 01月 12日

golang で AVIF 対応 image.Decode

標準ないし準標準 (golang.org/x/image/webp みたいな) にはないので面倒っぽそうだなあとなんとなく思ってたけど意外にも簡単に対応できた。

CGo を許容するかで今のところ2択っぽい

  1. _ "github.com/vegidio/avif-go" CGo
  2. _ "github.com/gen2brain/avif" libavif を WASM にコンパイルし、wazero で実行

どっちもブランクインポートするだけでつかえる。ベンチ的には CGo のほうが10倍早い

こういう検証とベンチをむちゃくちゃサクっとやれるのすごいいいよなあ。Agentic Coding。脳の負荷が位置が変わっているのを感じる。

検証

vegidio/avif-go

# register_vegidio_test.go
package main

import (
	"bytes"
	"image"
	"os"
	"testing"

	_ "github.com/vegidio/avif-go"
)

func TestRegisterVegidio(t *testing.T) {
	data, err := os.ReadFile("../../static/fixtures/sample.avif")
	if err != nil {
		t.Fatal(err)
	}

	img, format, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		t.Fatalf("Vegidio registration failed: %v", err)
	}

	t.Logf("Successfully decoded %dx%d %s image using vegidio", img.Bounds().Dx(), img.Bounds().Dy(), format)
	if format != "avif" {
		t.Errorf("Expected format avif, got %s", format)
	}
}
go test -v register_vegidio_test.go   
=== RUN   TestRegisterVegidio
    register_vegidio_test.go:23: Successfully decoded 2886x2164 avif image using vegidio
--- PASS: TestRegisterVegidio (0.08s)
PASS
ok      command-line-arguments  0.313s

github.com/gen2brain/avif

# register_gen2brain_test.go
package main

import (
	"bytes"
	"image"
	"os"
	"testing"

	_ "github.com/gen2brain/avif"
)

func TestRegisterGen2brain(t *testing.T) {
	data, err := os.ReadFile("../../static/fixtures/sample.avif")
	if err != nil {
		t.Fatal(err)
	}

	img, format, err := image.Decode(bytes.NewReader(data))
	if err != nil {
		t.Fatalf("Gen2brain registration failed: %v", err)
	}

	t.Logf("Successfully decoded %dx%d %s image using gen2brain", img.Bounds().Dx(), img.Bounds().Dy(), format)
	if format != "avif" {
		t.Errorf("Expected format avif, got %s", format)
	}
}
go test -v register_gen2brain_test.go 
=== RUN   TestRegisterGen2brain
    register_gen2brain_test.go:23: Successfully decoded 2886x2164 avif image using gen2brain
--- PASS: TestRegisterGen2brain (0.73s)
PASS
ok      command-line-arguments  1.119s

Cloudflare R2を使ってみる

容量的には 10GB までは無料で、そのあと従量課金になってもそれほど高額ではなさそう。

ただクラスB操作(参照系)がコントロールしにくいのでちょっと怖い。CDN経由のキャッシュミスだけが問題なのでファイル数が十分に少なければ恐るることはなさそう。現状ではこのサイトの全GETリクエスト(ほとんどクローラーだけど)がR2に飛んだとしても無料枠に収まる。

まず新規アップロードを R2 にするようにしてある。様子を見つつ過去分もアップロードするつもりではあるけど、その前に JPEG ファイルを全ファイル AVIF にするということをしたいのでまだやれてない。

もともと Cloudflare Registrar 契約してるので使いはじめに抵抗がない感じ。

CDNエッジでジオシティーズ的なやつを作ってみる

半年以上前に Cloudflare Workers上で Honoフレームワーク使いつつ懐しい構成のサイトを作ってみたやつ、日記に書いてなかった。

中身がシーディーエンヌのエッジのワーカーで動いてるけど見てくれがジオシティーズだったら面白いなと思って、当時は Github Copilot 使いながら作った記憶。

いわゆる6hotサイトみたいのは現状の Worker の無料枠で全然問題なかろうという気がするし、そういう意味では現代のジオシティーズなんじゃと思ったのだった。

Durable Objects まわり仕様が変化していくのでエーアイに書かせて困った思い出がある。作りっぱなし