Skip to content

feat(ghtkn): authenticate aqua, gh, and C-FO git through the ghtkn agent on business - #254

Merged
paveg merged 6 commits into
mainfrom
feat/ghtkn-agent-business
Oct 13, 2026
Merged

paveg merged 6 commits into
mainfrom
feat/ghtkn-agent-business

Conversation

@paveg

@paveg paveg commented Oct 13, 2026

Copy link
Copy Markdown
Owner

概要

business プロファイルの GitHub 認証を、ghtkn が発行する GitHub App の短命 token(8 時間)に切り替えます。aqua、gh、C-FO 配下の git の 3 経路が対象です。#223 で入れた keyring 方式(AQUA_KEYRING_ENABLED と aqua-token-sync)はこの PR で置き換えます。

7 月に ghtkn をやめたのは、8 時間ごとに Device Flow で再認証する手間が理由でした。ghtkn v0.3.4 以降は agent バックエンドで refresh token を使えるので、この再認証が不要になります。残る手間は、ログインや agent の再起動のたびに ghtkn agent unlock --enable-refresh で passphrase を 1 回入れることです。

personal プロファイルは対象外です。personal には aqua がなく、App の user token では他人のリポジトリに書き込めない(OSS への gh pr create が通らない)ためです。

flowchart LR
  launchd["launchd: local.ghtkn-agent"] --> agent["ghtkn agent"]
  aqua["aqua"] -->|"GHTKN_APP=C-FO/read"| agent
  gh["gh ラッパー"] -->|"ghtkn exec -e GH_TOKEN:C-FO/write"| agent
  git["git(C-FO のみ)"] -->|"git_owner: C-FO → C-FO/git"| agent
  agent --> github["GitHub"]
Loading

変更内容

  • home/dot_config/ghtkn/ghtkn.yaml.tmpl(新規): backend: agent と 4 つの App(C-FO/none、C-FO/read、C-FO/git、C-FO/write)。先頭の C-FO/none が既定になる
  • client_id はリポジトリに入れない。ローカルの chezmoi config の data.ghtkn_client_ids.{none,read,git,write} に手で書き、.chezmoi.yaml.tmpl が次の chezmoi init に引き継ぐ。値のない App はテンプレートが出力しない
  • aqua: グローバル設定に suzuki-shunsuke/ghtkn@v0.4.1 を追加し、.zshenv で AQUA_GHTKN_ENABLED=true と GHTKN_APP=C-FO/read を設定する。aqua には App を選ぶ設定がなく、指定しないと先頭の C-FO/none が使われて private registry を読めない
  • gh: features/ghtkn-gh.zsh で ghtkn exec -e GH_TOKEN:C-FO/write -- gh に包む。token を取れないときは gh を実行せず、unlock を促して止まる。GH_TOKEN や GITHUB_TOKEN を明示したときは素通しする
  • git: C-FO の作業ディレクトリでだけ !ghtkn git-credential を使う。それ以外の github.com は従来どおり gh auth git-credential と SSH
  • agent: LaunchAgent local.ghtkn-agent が aqua exec -- ghtkn agent start を常駐させる。run_onchange_after_load-ghtkn-agent.sh は plist か ghtkn のバージョンが変わったときだけ再起動する
  • 削除: features/aqua-token-sync.zsh とそのテスト

レビューの観点

  • git の includeIf の位置を移した(dot_config/git/config): credential helper のリストはファイル順に積み上がる。work の helper = リセットを gh のセクションより後ろに置かないと、ghtkn の helper が gh の helper に負ける
  • insteadOf の自己書き換え(dot_config/git/work.tmpl): business では github.com の HTTPS をすべて SSH に書き換えており、SSH では credential helper が呼ばれない。[url "https://github.com/C-FO/"] insteadOf = https://github.com/C-FO/ は最長一致で優先されるので、C-FO だけが HTTPS のまま残る
  • unset AQUA_KEYRING_ENABLED(dot_zshenv.tmpl): 行を消すだけでは、apply 前に起動した herdr や tmux のサーバーから新しいペインへ値が受け継がれる。aqua は keyring を ghtkn より先に調べるので、古い token で 401 になる。実機でこの現象を踏んだため追加した
  • GHTKN_APP=C-FO/read があっても、git は C-FO/git を使う。ghtkn は owner の一致を GHTKN_APP より先に評価するため(ghtkn-go-sdk の config/app.go の ResolveApp)

検証

just test と just fmt-check は exit 0 です。追加したテストは次の 2 本です。

  • tests/zsh/ghtkn-gh.test.sh: ラッパーの各分岐、business と personal の描画の差、App の出し分け、受け継いだ AQUA_KEYRING_ENABLED を .zshenv が消すこと
  • tests/zsh/ghtkn-git-helper.test.sh: C-FO だけが HTTPS に残ること、C-FO で ghtkn の helper が最後に来ること、personal には何も入らないこと

実装を 1 箇所ずつ壊して、テストが red になることを確認しました。

壊した箇所 結果
ラッパーから :C-FO/write を外す red
C-FO の insteadOf を外す red
client_id が空でも git の App を出力する red
.zshenv から unset AQUA_KEYRING_ENABLED を外す red(got: keyring=true ghtkn=true)

business の実機(worktree をソースに対象ファイルだけ apply)で確認した結果です。

経路 確認 結果
aqua キャッシュなし(AQUA_ROOT_DIR=$(mktemp -d))で clusterops の aqua i -l exit 0
gh ラッパー経路の token の先頭、gh api repos/C-FO/clusterops ghu_、取得できた
git GHTKN_LOG_LEVEL=debug git fetch --dry-run(clusterops) app_name=C-FO/git、exit 0
agent launchctl print、ghtkn info running、socket は ~/.cache/ghtkn/agent.sock

適用時の注意

  • chezmoi apply は ~/.config/ghtkn/ghtkn.yaml を上書きする。先に ghtkn_client_ids を埋めておかないと App が 0 件になり、3 経路すべてが止まる
  • 初回は ghtkn agent unlock --enable-refresh で passphrase を設定し、ghtkn auth C-FO/read、C-FO/write、C-FO/git を 1 回ずつ実行する。ghtkn exec と aqua は Device Flow を自分では始めない

🤖 Generated with Claude Code

paveg and others added 6 commits October 13, 2026 11:47
Prompt for the shared GitHub App client ID in business mode, render ~/.config/ghtkn/ghtkn.yaml with the agent backend, and install ghtkn through the global aqua config.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Business gh runs under `ghtkn exec -e GH_TOKEN` so the token only lives in the child env. aqua switches from the keyring token to AQUA_GHTKN_ENABLED.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A LaunchAgent starts the agent through aqua and an onchange script reloads it when the plist or the ghtkn version changes. The agent restarts locked, so the script reminds the operator to unlock it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ID map

Client IDs are carried forward from the local chezmoi.yaml (data.ghtkn_client_ids) and never enter the repo. ghtkn.yaml renders only apps with an ID, aqua selects C-FO/read through GHTKN_APP, and gh uses C-FO/write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keep C-FO URLs on HTTPS with an insteadOf override of the global SSH rewrite, and give them a path-aware ghtkn helper. The work include moves below the gh helper sections so its helper reset applies last.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shells started under a multiplexer that predates the switch to ghtkn
still inherit AQUA_KEYRING_ENABLED=true. aqua checks the keyring before
ghtkn, so it sent the stale keyring token and the private C-FO registry
failed with 401 Bad credentials.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 13, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 5b3f6d1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@paveg
paveg merged commit 53ff735 into main Oct 13, 2026
13 checks passed
@paveg
paveg deleted the feat/ghtkn-agent-business branch October 13, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant