おそらくはそれさえも平凡な日々

RSS新着をまとめてMarkdown化するmetabol

https://github.com/Songmu/metabol

このAI Agent時代にエンジニアたちが少なからず自分用のRSSリーダーを開発している雰囲気を感じていますが、私も作ってしまいました。とは言っても、作ったのはリーダーではなく、RSS新着をまとめてMarkdown化してローカル保存してくれるツールです。名付けて metabol。 Feedを食べて蓄える。ただしメタボり過ぎに注意、という所。

やることは、設定ファイル上に一覧されたRSSを読んで、新着をMarkdown化してローカルに保存してくれるだけ。閲覧は別のツールでやれば良い。テキスト化されていれば何でもできる。

取得状態の管理とかもやりたくないので、基本的に指定した日付に対する固定ウィンドウ内の記事を取得するだけにしている。それを日次で動かす想定。

使い方

インストール

ローカルで動かす場合、brew等でインストールできます。内部で mdhq コマンドも使うのでそれもインストールしてください。

$ brew install Songmu/tap/metabol
$ npm install -g @songmu/mdhq

セットアップ

適当なディレクトリで、 metabol init を実行すると metabol.yaml という設定ファイルの雛形が作成されます。

$ metabol init
Created metabol.yaml.

Next steps:
  1. Edit metabol.yaml and replace the example source list.
  2. Install mdhq if needed: npm install --global @songmu/mdhq
  3. Run metabol.

設定ファイルの編集

生成される metabol.yaml を以下のように編集します。ここでは私の2つのブログを対象にしています。ブログURLでも極力RSSを見つけようとしますが、RSSのURL直指定のほうが本当はおすすめです。

timezone でタイムゾーンを、window.daily で取得する区切りの時刻を設定します。これにより、以下の例では実行時に日本時間の前日の朝7時から当日の7時までの間に公開された記事を取得するようになります。catchuptrue にすることで、当日の7時以降から現在までの記事も取得してくれます。

# yaml-language-server: $schema=https://raw.githubusercontent.com/Songmu/metabol/main/schema.yaml

timezone: Asia/Tokyo # Optional, but recommended.
# catchup: false # Include the ongoing window up to the latest feed contents.

window:
  daily: "07:00"

sources:
  - https://songmu.jp/riji/
  - https://blog.song.mu/

その他、 --at オプションで、取得日付を選べたり、 --window-count オプションでまとめて複数日数分の記事を取得することも可能です。

実行例

先の設定ファイルが配置されたディレクトリで metabol を実行してみます。私のブログはそこまで更新頻度が高くないので、50日分まとめて取得してみます。ホスト毎への取得インターバルなどは気をつけているつもりなので、多少時間はかかります。

$ metabol --window-count 50
{"requestedUrl":"https://blog.song.mu/entry/agents-md-and-agent-skills","sourceUrl":"https://blog.song.mu/entry/agents-md-and-agent-skills","path":"blog.song.mu/entry/agents-md-and-agent-skills.md","status":"saved"}
{"requestedUrl":"https://songmu.jp/riji/entry/2026-08-11-codoc.html","sourceUrl":"https://songmu.jp/riji/entry/2026-08-11-codoc.html","path":"songmu.jp/riji/entry/2026-08-11-codoc.md","status":"saved"}
{"requestedUrl":"https://songmu.jp/riji/entry/2026-08-14-voice-input-with-foot-pedal.html","sourceUrl":"https://songmu.jp/riji/entry/2026-08-14-voice-input-with-foot-pedal.html","path":"songmu.jp/riji/entry/2026-08-14-voice-input-with-foot-pedal.md","status":"saved"}
{"requestedUrl":"https://songmu.jp/riji/entry/2026-08-23-tagpr-documentation.html","sourceUrl":"https://songmu.jp/riji/entry/2026-08-23-tagpr-documentation.html","path":"songmu.jp/riji/entry/2026-08-23-tagpr-documentation.md","status":"saved"}
{"requestedUrl":"https://songmu.jp/riji/entry/2026-09-06-mdhq.html","sourceUrl":"https://songmu.jp/riji/entry/2026-09-06-mdhq.html","path":"songmu.jp/riji/entry/2026-09-06-mdhq.md","status":"saved"}

標準出力へのログ出力の通り、5件の記事が取得され、Markdown化されて保存されました。tree コマンドで確認すると、以下のようにディレクトリが作成され、記事が保存されていることがわかります。

$ tree .
.
├── blog.song.mu
│   └── entry
│       └── agents-md-and-agent-skills.md
├── metabol.yaml
└── songmu.jp
    └── riji
        └── entry
            ├── 2026-08-11-codoc.md
            ├── 2026-08-14-voice-input-with-foot-pedal.md
            ├── 2026-08-23-tagpr-documentation.md
            └── 2026-09-06-mdhq.md

6 directories, 6 files

テキストになっているので、あとはAIにサマリを作ってもらったり、翻訳してもらったりもやりやすいはずです。私は毎日新聞的に一枚のMarkdownにまとめてもらうなどしています。

GitHub Actions

metabol はGitHub Actions上で簡単に動かすためのカスタムアクションを提供しています。

まず注意点として、サーバー負荷やコンテンツ著作権の観点から、これを頻度高く、そして、公開リポジトリで動かすことは避けて下さい。

以下は、毎日7時15分に実行し、取得結果をコミットするworkflowの例です。手動で実行する場合は、 workflow_dispatchat 入力を指定することで、任意の日付の取得も可能にしています。

name: Collect feeds
on:
  schedule:
    - cron: "15 7 * * *"
      timezone: Asia/Tokyo
  workflow_dispatch:
    inputs:
      at:
        description: Date selecting the window to process (YYYY-MM-DD)
        type: string
jobs:
  collect:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v7
      - id: metabol
        uses: Songmu/metabol@v0
        with:
          config: metabol.yaml
          root: articles
          timezone: Asia/Tokyo
          catchup: true
          at: ${{ inputs.at }}
      - name: Commit and push articles
        if: ${{ !cancelled() && steps.metabol.outputs.count > 0 }}
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add articles
          if git diff --cached --quiet; then
            exit 0
          fi
          git commit -m "Update articles"
          git push

是非ご利用下さい。

設計思想

取得タイミングなどの状態管理、スケジューラー、SQLiteなどのデータベースを持たない単純なツールを作りたくてこうなった。

内部的にはRSSからURLを抽出する rssnip と、URLから本文を抽出してMarkdown化する @songmu/mdhq を組み合わせたcomposition的な構成になっているのも特徴的だと思う。

RSS回帰(?)の流れ

最近、特にAI関連での情報の流れが早いので、多くの人が改めてRSSを含めた情報収集パイプラインを組み直しているのを感じていますし、私もその一人です。

改めて購読RSSリストをメンテナンスしていこうと思っていますが、おすすめあったら教えてください。

WebページをMarkdownクリップしてローカル管理する `mdhq`

WebページをMarkdown化してローカル管理するCLIを作りました。名前はmdhq。Markdownの md と、ghqhq からあやかっています。

その名の通り、WebページのURLからローカルパスをghqチックなルールで導出し、そこにMarkdownを保存してくれるものです。ファイルシステム完結のナレッジベースを作るために開発しました。

使い方

npmからインストールできます。

npm install --global @songmu/mdhq

ghq同様に mdhq get <url> でWebページをMarkdown化してローカルに保存します。$MDHQ_ROOT 環境変数や設定ファイルで保存先を変更できます。デフォルトでは画像類もダウンロードします。

$ mdhq get https://songmu.jp/riji/entry/2026-08-23-tagpr-documentation.html
${MDHQ_ROOT}/songmu.jp/riji/entry/2026-08-23-tagpr-documentation.md

標準入力経由で複数取得もできるので、他のツールとも組み合わせやすい。

$ cat urls.txt | mdhq get

mdhq list で一覧もできる。

$ mdhq list
blog.song.mu/entry/agents-md-and-agent-skills.md
songmu.jp/riji/entry/2026-08-23-tagpr-documentation.md

細かい使い方はドキュメントを参照して下さい。

作った動機

RSSの新着記事やWeb Clip管理を効率化したく、まずは保管システムを作ったという所。管理のためにSQLiteなどを使う手もあるけど、ファイルシステムで完結した方が並列での取得や、CIでの実行なんかもやらせやすいし、Git管理もやりやすいのでそれで良いのではないかと思った次第。

Markdown化すれば要約とかも作りやすい。WebページのMarkdown化は Defuddle というObsidian Web Clipper で使われているライブラリを利用している。これを使いたかったから今回はTypeScriptで書いた。

今後の展望

ghqがそうであるように、これはあくまで保管システムなので、取得管理や閲覧、検索などは別のツールを組み合わせて使うことを想定している。閲覧や検索については、僕の場合、ObsidianのVault内に保存するようにした。

取得管理などは、RSSリーダーなどとの連携ができると便利なので、今後取り組んで事例紹介したい。

Conditional Get管理とかもある程度ケアするようにしているつもりだが、余りちゃんと検証はできていない。

あとは、リダイレクトやCanonical、クエリー文字列などをケアしたURLの正規化などもある程度やってはいるのですが、仕様を調整するかも知れません。

相変わらずのニッチなオレオレツールですが、興味がある方は使ってみて下さい。

tapgrのドキュメントを整備しました

GitHub - Songmu/tagpr: automatically creates and updates a pull request for unreleased items, tag them when they are merged, and create releases.

おかげさまで、tagpr は多くの場所で導入いただいています。ただ、機能も増えてきて、その割にはドキュメントが不十分だと言う指摘があった為、READMEをアップデートすると共に、ドキュメンテーションサイトを作成しました。

https://junkyard.song.mu/tagpr/

主要なユースケースや躓きポイントなどがまとめられています。

かなり、GitHub Copilotと相談しながら書いてもらった感じにはなっているので、もう少し自分の言葉で書いたヤツをtagpr handbookの様な形でまとめたいと思っています。それを元にまたドキュメントサイトを改善する、みたいな流れになれば良いかなと思っています。

tagpr、便利なので、これから使いはじめる方の参考にもなると幸いです。

関連

フットペダルデバイスと最近の音声入力環境

AI Agentに指示を与えるために音声入力を使うことが増えてきたので、フットペダルを導入した。左手デバイスで有名なStream Deckシリーズのこのフットペダル。

ペダルを踏んでいる間は音声入力ソフトの入力モードになるように設定して、AI Agentのプロンプトだけではなく、任意のエディタに対して音声入力できるようにした。とは言え、文章作成には私はうまく使いこなせないが、Agentに渡すプロンプトなどは、多少誤字があってもほぼ問題ないのでうまくいっている。

音声入力ソフト Handy

音声入力には、HandyというOSSを使っている。これはダウンロードしたSpeech to Textモデルを使うので、ローカル完結できて無料で使える。とても便利なので作者をスポンサーした。

Sponsor @cjpais on GitHub Sponsors

設定画面はこんな感じ。Option + Space でバックグラウンドで音声入力を受け付けてくれるようになるので、それをフットペダルに割り当てている。言語は自動検知してくれるモデルを利用していて、日本語も英語もいける。

モデルはNVIDIAのNemotron Streaming 3.5を使っている。精度はかなり良い。他のモデルも設定画面から色々選べて、選んだものを簡単にダウンロードしてきてくれる。Whisperとかも選べる。

AI Agent自体が音声入力機能を備えることも増えてきたが、別ソフトウェアに分かれていると複数のエディタ環境にまたがって利用することができるし、アーキテクチャ的にも疎結合できれいだと思う。もちろん、密結合されていることでインテグレートされた体験を提供できたりもする場合もあるわけですが。

何にせよ、このフットペダルとHandyを組み合わせた構成はおすすめです。

codocを導入してチップを送ってもらえるようにしました

このブログと、はてなブログのサブブログ、とポッドキャスト、趣味でOSSをやっている者だ にチップボタンを導入して少額での支援を受けられるようにしました。いわゆる投げ銭的なやつなので、送ってもらえると嬉しいです。メッセージも送れます。

今回利用したcodoc は、はてなブログと連携したときに知って、アカウントを作っていた。なので、今回は画面の指示に従ってタグを貼り付けるだけで簡単に導入できた。個人サイトに簡単に追加できるのは嬉しい。codocは社長の石川さんに以前お世話になったこともあって、応援しています。

有料記事販売みたいなペイウォールを設けるのは、私自身の手間の観点からもやろうとは考えていないのですが、codocがそういう限定コンテンツの有料販売以外の用途でも使えることに気付いて導入した次第です。送ってもらった体験がどういう感じになるかや、正しく設定できているかなどが分からないので、是非100円でもお送りいただけると嬉しいです。

Markdownからpptxを出力するslidown

最近は仕事でPowerPointを使うことが増えたのだが、やはりMarkdownからプレゼンテーションを作りたいという偏執狂的な欲求が抑えられなくなったのと、私も開発に参加しているk1LoW/deckの快適な体験をPowerPointでも得たくなったため、deckと同様のMarkdownからpptxを出力するOSSを作った。名前はslidown。

https://github.com/Songmu/slidown/

Go製なのでgo install でも入るが、例によってHomebrewでもインストールできるようにもした。

$ brew install tap/Songmu/slidown

deckの「デザインとコンテンツの分離」という素晴らしいコンセプトを踏襲し、スライドマスタでデザインを調整する思想となっている。

使い方

deckと同じ様な、--- をスライド区切りとしたMarkdownを使えば、pptxが出力できる。ローカル完結なので、 slidown new などもなく slidown apply でいきなりpptxを出力できる

$ slidown apply slide.md
Wrote slide.pptx (11 slide(s))

出力先の指定や、テンプレートファイルの指定 (potxやpptx)も可能。--watch モードもあり、Markdown編集を見て、pptxを自動で出力し直してもくれる。詳細はリポジトリやドキュメントを参照して欲しい。

slidown apply slide.md --template template.potx

オープンフォーマットとしてのpptx

pptxは主にXMLを中心とするファイル群をzipでまとめたOffice Open XML (OOXML)というオープンフォーマットであり、ECMA-376で定められている。Microsoft 365 (Office) の他にも LibreOffice なども読み書きができる。おそらくそれぞれ独自拡張などはあると思うが詳しくはない。結局全部XMLに中には収まってはいるのは確かだ。

なので、実はローカル完結で機械的に読み書きさせすい。slidownの出力スピードもとても早い。仕様を確認しながらの実装は大変だが、今ならAIを使えばそれほど難しくない。

どの様にslidownを作ったか

実はslidownは完全にdeckのforkです。GitHubのfork機能は作っていないが、deckのリポジトリを引き継いでいます。古い履歴にはdeckの履歴が残っています。ライセンスなども引き継いでいます。

fork当時の最新のコミットからGitHub Copilotに一気に書き換えてもらいました。実装を簡単にするためと、deck自体にpptx出力機能が欲しいかと言うと、そうでもないなと思ったので、一旦別プロダクトとすることにしました。AI Slop Fork的なことをやったわけだけど、まあ、私もdeckの開発にはだいぶ参加させてもらったので大丈夫かなと思っています。

兄弟プロダクトとして、コラボレーションなり共通化なりはできるところはできると良いかと思っている。今回の実装の中でも、ヒントがえられた部分もあったので、deck側にアイデアとして戻せても良いかな、と思っています。

使い方などは別途まとめたいと思っていますし、まだできたばかりで不安定だとは思いますが、今の段階でも結構便利なので、是非ご利用ください。

仕事用のフォントを合成して公開してみた

https://github.com/Songmu/OctoBiz

GitHub社はMona Sansというオープンソースのフォントを公開していて、社内で作られて公開されるプレゼンテーションはこのフォントが使われることが多い。ただ、このフォントには当然日本語のフォントが含まれておらず、プレゼンテーションを和訳する際には他のフォントを使わざるを得ず、テイストが変わってしまうのが気になっていた。

なので、このMona Sansと日本語フォントを合成し、日本語の資料に使うためのフォントを作った。合成する日本語フォントは、モリサワのユニバーサルデザインフォントのBIZ UDゴシックのプロポーショナル版のBIZ UDPゴシックを採用し、OctoBizと命名した。今後業務で使っていけると良いかなと思っている。

BIZ UDPゴシックは400(Regular)と700(Bold)の2種類を提供しているので、OctoBizもStaticフォントとしてその2種類を用意しています。GitHub Releases からダウンロードしても良いですし、Homebrewでインストールもできるようにもしました。

$ brew install Songmu/tap/octobiz

作ってみて、フォント周りの知識が少し増えたので面白かった。

インスパイア元

私は今は白源/HackGenというプログラミングフォントを使っているのですが、このフォント作者のyuru7さんが、BIZTERという合成フォントを作られていて、そのリポジトリ内の合成用スクリプトを流用させてもらいました。なので、アイデア・着想・手法すべてBIZTERから頂いています。ありがとうございます。

OctoBizのリポジトリはBIZTERのリポジトリを参考にしつつ、簡単な依存管理的な仕組みと、tagprによるリリースの自動化も実現できて満足している。Homebrewでのインストールも簡単にできて良かった。

余談

GitHubはMonaspaceというプログラミングフォントも公開しているので、これもまた日本語フォントと合成してみようかと思っていたら、yuru7さんがすでにMoralerspaceというフォントを作られていた。バリエーションも色々あって良い感じなので、プログラミングフォントはこちらに乗り換えようかと検討している。

感謝の意味も込め、yuru7さんにはGitHub Sponsorさせてもらいました。ありがとうございます!

サプライチェーンアタック対策とdependabot活用

注: 本記事は執筆時点(2026年4月)の情報をもとに書いています。実際のご利用にあたっては、公式ドキュメント等の最新情報を参照し、正確性を確認の上ご利用ください。

さて、axiosへの攻撃の件で、サプライチェーンアタックの恐ろしさを改めて感じさせられました。外部ライブラリを使う場合、基本的にはセキュリティ面も含めて最新バージョンを使いたいわけですが、その更新作業は脆弱性が入り込みやすいタイミングでもあるというジレンマがあるわけです。

なので、以下のようなポリシーとフローでの外部ライブラリ利用が現状の推奨要件と言えるでしょう。

  1. 基本は最新バージョンを使う
    • 機能面、パフォーマンス、セキュリティ面でより良い
    • 追随を怠ると更新が困難になり、新機能が使えないだけではなく、セキュリティリスクも高まる
  2. ただし、新バージョンリリース直後ではなく、しばらくしてから最新版を適用する
    • 最新バージョンに問題が無いか様子見をしてから入れるということ
    • 特に最近は単なるバグや脆弱性だけではなくmalwareが仕込まれるケースが出てきた
  3. 検証してから最新バージョンに更新する
    • CIでテストが通るか・おかしな挙動が無いか確認してからコードベースに反映する
    • その後、開発者の手元、本番環境でも更新をおこなう

特に2点目が最近よく言われるようになってきたポイントです。"Minimum release age" や "cooldown" と言われる、リリースから一定の期間経過後に最新バージョンを入れるというものです。dependabotやrenovateなどの更新ツール、JavaScript周辺のパッケージマネージャーは一通りそのための機能をすでに備えています。

3点目の「検証してから最新バージョンに更新する」も簡単に書きましたが、厳密に考えると少し厄介です。検証時点でmalwareが混入する可能性があるからです。今回のaxiosの件もまさにそういうケースでした。

そう考えると先程の要件を一通り満たすためには以下のプラクティスが必要です。

  1. 自動的にバージョンアップが促されるワークフローを構築する
  2. "Minimum release age" 設定ができるツールを利用する
  3. 秘匿情報にアクセスできない隔離された環境で新バージョンの検証を行う

これらをDependabotで実現する方法を紹介します。

Dependabotでの実践例

Dependabotを導入し、自動的にバージョンアップが促されるようにする

Dependabotは依存ライブラリ更新のワークフローを構築するGitHub組み込みの機能であり、無償で利用できます。定期的に依存関係の更新をチェックし、PRを起票してくれます。

以下のようなYAML設定を配置するだけで利用を開始できます。

# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
  directory: "/"
  schedule:
    interval: "weekly"
  cooldown:
    default-days: 7

上記はnpmに対して週次でチェックをするシンプルな設定例ですが、チェック頻度の変更、他のパッケージシステムでの利用、グルーピング設定なども出来ます。詳しくはドキュメントを参照ください。

Dependabot quickstart guide - GitHub Docs

cooldown項目でminimum release ageを設定する

先のYAMLの例の中にも書いていましたが、cooldown項目で、リリースから一定期間経過してから最新バージョンを入れるようにできます。これも細かい設定が可能で、例えば、一部のライブラリは除外したり、上がったバージョンがメジャーバージョンかマイナーかで期間を変えたりもできます。これもドキュメントを参照してください。

Dependabot options reference - GitHub Docs

dependabotが起票したpull request上で検証を行う

dependabotはpull requestの形でバージョンアップの提案をしてくれます。ですので、そのPR上のGitHub ActionsのCIワークフローで検証を行えます。

dependabotはリポジトリシークレットにアクセスできない

ここで嬉しいのは、このPR上のワークフローは外部のforkリポジトリからのPRと似たような権限モデルで動くことです。具体的にはリポジトリシークレットなどにはアクセスできないため、そこからトークン等が漏洩する心配はありません。GITHUB_TOKEN もデフォルトではread-onlyになります。

Dependabot が更新する依存パッケージには信頼できないコードが含まれる可能性があるため、fork からの PR と同等に扱うということなのでしょう。その点では、package.json などのバージョンファイルを開発者が更新してPRを起票するより、dependabotに任せた方が安全とも言えます。詳しくは以下を参照してください。

Troubleshooting Dependabot on GitHub Actions - GitHub Docs

github actions workflow runs that are triggered by dependabot from push, pull_request, pull_request_review, or pull_request_review_comment events are treated as if they were opened from a repository fork.

Dependency reviewと組み合わせる

パブリックリポジトリや、GitHub Advanced Security (GHAS) の契約がある場合、dependency reviewを使えば、PR上の依存関係変更に対するレビューを追加でおこなえます。具体的にはGitHub Advisory Databaseを参照し、依存関係に脆弱性がないかをチェックしてくれます。これもPR上で動くワークフローで実行できるため、問題がある場合に変更をブロックできます。

# .github/workflows/dependency-review.yml
name: Dependency Review
on: [pull_request]
permissions:
  contents: read
jobs:
  dependency-review:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v6
      with:
        persist-credentials: false
    - uses: actions/dependency-review-action@v4

Dependency reviewには、自分たちのコードベースに望ましくないライセンスのコードの混入を防ぐ機能などもあります。OSSだと無償なので積極的に使うと良いでしょう。詳しくはドキュメントを参照してください。

About dependency review - GitHub Docs

ローカルマシンのリスク軽減

ここまで説明した通り、依存ライブラリの安全性を確認してから、各開発者のローカルマシンに展開していくのが安全なフローです。

ただ、開発者やローカルのコーディングエージェントがこのフローを守らず手元でうっかりアップデートしてしまう場合には無力です。それを防ぐためにはやはり、.npmrc など、手元の開発環境の設定も合わせてしっかりやることも重要です。

また、AIコーディングエージェントはかなり気軽にnpm installpip install 等を実行するため、変なサイトに情報を流さないようにサンドボックス機能があるとやはり嬉しいところです。先日紹介したfenceのようなサンドボックスツールを活用するのも一つの方法でしょう。

コードベース窃取等の外部送信への対策

どこまで気にするかという話もありますが、今回のaxiosのようにシークレットを狙うのではなく、コードベースを丸ごと盗もうとするようなmalwareの場合には、上記の方法だけでは不十分です。ただ、こういった無差別型の攻撃の場合、攻撃者はコードベースよりシークレットを窃取しようとすることが多いため、気にするべきリスクの優先順位としては低くはなります。

ただ、その辺りも含めて外部への情報送信リスクを懸念するようであれば、CI環境で怪しいサイトへの通信をブロックすることでリスクを低減できます。GitHub ActionsのGitHub-hosted runnerの場合、etc/hosts ファイルのプロビジョニングで怪しいサイトへの通信はブロックするという基本的な対策は入っています。

更に追加で自分たちで監査したり、許可・拒否リストの管理をしたいケースでは、サードパーティのStepSecurityのHarden-Runnerなどの導入が手軽ですが、プライベートリポジトリの場合は有料です。他には、GitHub Actionsからの外部通信をAzure private networking経由にする方法Self-hosted runnerで自分たちでしっかりネットワーク制限をかける方法もありますが、運用コストは上がります。

これについての本命として、GitHubも最近発表したセキュリティロードマップの中で、"Native egress firewall for GitHub-hosted runners" を年内くらいのターゲットで提供予定であることを公表しているので、期待したいところです。それ以外の下記の記事に記載されているロードマップ機能にはいずれも期待しています。

What's coming to our GitHub Actions 2026 security roadmap

fence - AI AgentをOSサンドボックスの中で動かす

AI Agentも賢くなってきたとはいえ、ローカルで何をしでかすかわからない怖さは拭いきれない。かと言って、細かく認可を与えるのもめんどいし、ザルな見過ごしも起こりやすくなって危ないので、できればファイル・ネットワークアクセス、コマンド実行等に適切に制限をかけたサンドボックス環境で放し飼いにしたい。

CLIとして動かすAI Agentの場合、引数に指定したコマンドをサンドボックス内で動かすコマンドラッパーがあると嬉しい。自作しようかと思っていたが fence というツールがまさしくそれだったので、これを使うことにした。Goで書かれていて、macOSとLinuxをサポートしている。早速、GitHub Copilot CLIでも利用しやすいようにpull requestを送って、取り込んでもらった。

使い方

使い方は簡単で fence コマンドの引数にサンドボックス内で動かしたいコマンド、今回のユースケースであればAI AgentのCLIを指定するだけだ。

$ fence copilot
$ fence claude

ただ、最初は外部へのネットワークアクセスが遮断されているので最低限の設定ファイルは必要だ。これも簡単で、fence config init とすれば ~/.config/fence/fence.json に初期設定を書き込んでくれる。初期設定は以下のように簡素だ。

// Starter config generated by `fence config init`; this file extends "code".
// Rules from "code" are inherited and not shown below.
// Add your project-specific overrides in this file.
// Run `fence --list-templates` to see available templates.
// Configuration reference: https://github.com/Use-Tusk/fence/blob/main/docs/configuration.md
{
  "extends": "code"
}

これは、code というCoding Agent用のビルトインテンプレートを継承している。このテンプレートの中身は以下から参照可能だ。特定のAI関係のドメインのみにアクセス許可を与え、ローカルの秘匿ファイルへのアクセスや git push などのコマンドは拒否するようになっている。実際の項目ごとの設定内容は fence config show で確認できる。ここから適宜調整すると良いでしょう。

https://github.com/Use-Tusk/fence/blob/main/internal/templates/code.json

動作確認とデバッグログ

fence の中でAI Agentを動かしていると、ファイルやサイトアクセスのブロック起因で意図しない挙動になることがある。それを確認するために、--monitor--debug オプションをつけて起動すると、モニターログが出力され、実際にブロックされたファイルやサイトを確認できる。ログは標準エラー出力に出るので、ファイルにリダイレクトして、tail で確認するとよいでしょう。ブロック状況を確認して、必要に応じて許可設定を追加すると良いでしょう。

$ fence --debug --monitor -- copilot 2>> .fence-monitor.log
$ tail -f .fence-monitor.log

Appendix

同種のソフトウェア

fence は600 starsなので、srtやnonoほどではないが、十分に信用がおけると判断し、Go製であることが私にとっては好ましいので採用することにした。

ちなみに、もともとはcageの存在だけ認識していて、そのネットワーク版を作ろうと思ってプロキシ設計までやっていたのだけど、調べたら同種のソフトウェアが流石にあるということが分かったのでそれに乗ることにしたというのがある。ラッパーコマンド自身がプロキシになってそれ経由の通信しか通さなくするというアーキテクチャは面白いので作ってみたかったが、こういうツールは既存の成熟したものを使う方が安心。

いずれのツールでもmacOSではApple SeatbeltというOS組み込みのサンドボックスを使っている。非推奨な機能らしいが、CLIでは代替手段が提供されてておらず、多くのツール内で使われているため、もはや廃止は難しそうになっているようだ。

Linuxではsrtとfenceはbubblewrap, nonoとcageはLandlockを使っている。

この辺りは以下の記事が参考になります。

Claude Code組み込みのサンドボックス機能を使えばよいのでは?

これまでの説明どおり、内部的には同様のことをしているのでそれで良いと思います。まあ、こういうのは別の専用のツールに分かれていた方が責務分離的に安心だし、細かい設定も可能だというのはあるでしょう。

というより、私が主に使っているGitHub Copilot CLIがまだサンドボックス機能が無いので、使っているというのが現実でもあります…。Copilot CLIのissueでも実装も望まれているので、そのうち機能追加されるとは思いますが、早めの対応を期待したいところです。

Add sandbox mode to restrict Copilot CLI file access to a specified working directory · Issue #892 · github/copilot-cli

ファイルにプロンプトを書き込むと非同期にAI Agentが召喚されるツールを作った

Markdownの中に 「@copilot 〇〇について調べて」みたいなプロンプトを書けば、コーディングエージェントが自動でそれを認識して非同期で動作してくれると嬉しい。なので、そういうツールを作った。それが ghsummon。その名の通り、手元からAI Agentを召喚するツールで、GitHub Actions上で動かす。

https://github.com/Songmu/ghsummon

@copilotで始まる行がGitHub上にpushされると、pull requestが自動で作成される。該当行をプロンプトとして認識し、GitHub Actions上でCopilot coding agentが動き、pull request上でファイルを編集してくれるという仕組みだ。

動作イメージのpull requestがこちら。"Who is @Songmu" というプロンプトに対して、GitHub Actionsが動き、README.mdを編集してくれている。

これは、GitHub Copilot coding agentというGitHub Actions上でCoding Agentを動かす機能を単に利用しているだけなのですが、ファイルにプロンプトを書き込んでpushするだけでAIにタスクを依頼できるのは便利。

私の利用法

私は日常のメモやドキュメントを Obsidian で書き、Gitプラグインで定期的にGitHubにpushしている。なので、プロンプトをファイルに書いておけば、定期pushのタイミングで自動的にCoding agentが動き始めてくれるようになり早速捗っている。

GitHub Copilot coding agentではカスタムエージェントも利用でき、サブエージェントも動作できる。なので、調査タスクなどを任せる場合、検証・再調査ループを回してくれるリサーチ用のカスタムエージェントを定義しておけば、かなり精度の高いレポートを生成してくれる。

pull request上で生成された内容を確認できるのも嬉しい。何か追加調査や調整してもらいたかったら、pull requestのコメント上で @copilot 〇〇について最新の情報を調べて追記して 指示を与えれば、さらに調査を続けて内容を更新してくれる。内容を確認してからマージすればよいので、調査をやらせっぱなしにして内容を読まないで腐らせてしまうことが減るのも嬉しいポイントだ。

使い方

リポジトリに以下のようなGitHub Actionsのワークフローファイルを置けばよい。あとはリポジトリ内でCopilot coding agentを動かすために、 copilot-setup-steps.yml というファイルも置く必要がある。詳細はREADMEを参照して欲しい。

name: ghsummon
on:
  push:
    branches: [main]
    paths: ['**.md']

permissions:
  contents: write
  pull-requests: write

jobs:
  research:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
        with:
          persist-credentials: false
      - uses: Songmu/ghsummon@v0
        with:
          token: ${{ secrets.GHSUMMON_TOKEN }}

また、APIでpull requestにcoding agentをアサインするために、PAT(Personal Access Token)を作る必要があるのが現状ちょっと悩ましいポイントではあります。

注意点

GitHub Copilot coding agentを動かすので、GitHub Actionsの実行時間やCopilotのプレミアムリクエストを消費します。

同じファイルに対してはpull requestとリモートブランチが生きている限り重複実行はされません。なので、逆に該当pull requestをマージなり閉じたりしたあとは、ブランチを削除しないと、そのファイルにプロンプトを書いてpushしても反応してくれないので注意が必要です。

今後の展望

今は、Markdownファイルにプロンプトを書く私のユースケースに最適化されているが、コードファイルへのプロンプトもサポートできると嬉しいかも知れない。例えば // @copilot ここ実装して みたいな具合。

また、現状は、pushされたコミットから新しいブランチを切るようになっているが、ブランチを維持してほしい場合もあるだろう。あとは、 @copilot 以外のエージェントも呼び出せるとかも。

そのあたりはissueに積んでありますが、使ってみて要望などがあればご意見いただけると嬉しいです。