Backlog権限スイッチャーCLI。Grantedの assume と同じ手触りで、「必要なプロジェクトに・必要な権限だけで・必要なときだけ」仮想ユーザー(サービスアカウント)をプロジェクトへ参加させる個人用ツールです。
Backlogでは「何ができるか(閲覧のみ/編集可)」はユーザーアカウント自体のスペースレベル設定で決まり、プロジェクト単位で読取専用の指定はできません。マスターユーザー(人間)がAPIで操作できるレバーは「プロジェクトへの参加/除名」だけです。
そこで強さの異なる仮想ユーザー(サービスアカウント)を2つ用意し、どちらを参加させるかで権限レベルを表現します。
| 仮想ユーザー | 種別 | 制限 | 用途 |
|---|---|---|---|
| reader | 一般ユーザー | 課題の閲覧のみ | permission = read |
| writer | 一般ユーザー | 制限なし | permission = write / admin |
bswitch switch <profile> を実行すると:
- config定義済みの全プロジェクトから両仮想ユーザーを除名(排他動作)
- 対象プロジェクトへ、permissionに応じた仮想ユーザーだけを参加させる
- 現在のシェルに
BACKLOG_API_KEY/BACKLOG_SPACE/BACKLOG_DOMAIN/BACKLOG_PROJECTを反映する
APIキーを常時保持させてよい構造です。キー自体の能力が「参加状況」で絞られるため、常時強い権限のキーを渡すより安全になります。
推奨: リポジトリをクローンせずにインストール
uv tool install git+https://github.com/ice1203/backlog-switcher以後 bswitch コマンドをPATH上のどこからでも直接実行できます。
bswitch list
bswitch switch customer-a開発用: editable install
cd /path/to/backlog-switcher
uv tool install -e .ソースコードを変更すればすぐ反映されます。
~/.config/backlog-switcher/config(INI形式)を作成します。パスは環境変数 BSWITCH_CONFIG で上書き可能です。
[default]
space = your-space.backlog.jp ; スペースのホスト名
writer_user = svc-writer@example.com ; 制限なし仮想ユーザー(メールアドレスまたは数値ID)
reader_user = svc-reader@example.com ; 「課題の閲覧のみ」仮想ユーザー(同上)
writer_api_key_ref = op://MyVault/backlog-svc-writer/credential ; 1Password参照
reader_api_key_ref = op://MyVault/backlog-svc-reader/credential
default_duration = 8h ; 任意。--duration未指定時の有効期限(未設定なら8h)
[profile customer-a]
project = CUSTOMER_A ; Backlogプロジェクトキー
permission = read ; read | write | admin
[profile customer-b]
project = CUSTOMER_B
permission = writeconfigが存在しない場合、bswitch はサンプルconfigをstderrに表示して終了します(自動生成はしません)。
BSWITCH_MASTER_API_KEY にマスターユーザー(人間)自身のBacklog APIキーを設定します。bswitchが読む唯一の入力用キーです。
export BSWITCH_MASTER_API_KEY="...".zshrc に以下を追記します(詳細は「5. シェル統合のセットアップ手順」)。
eval "$(command bswitch shell-init zsh)"bswitch の対話UI・警告・エラー・list/status の表示結果はすべてstderrに出力されます。stdoutには(シェル関数がevalする)export/unset行のみが出力されます。
# 1. マスターキー(必須。bswitchの全操作に使う)
export BSWITCH_MASTER_API_KEY="<マスターユーザーのAPIキー>"
# 2. 仮想ユーザーキー(op://を使わない場合)
export BACKLOG_WRITER_API_KEY="<writerのAPIキー>"
export BACKLOG_READER_API_KEY="<readerのAPIキー>"
# 3. シェル統合(.zshrcに追記すると、switch後に環境変数が自動セットされる)
eval "$(command bswitch shell-init zsh)"# どんなプロファイルがあるか確認
bswitch list# プロジェクトAを読み取り専用で操作したい
bswitch switch customer-a-read
# → BACKLOG_API_KEY にreaderのキーがセットされる
# → BACKLOG_PROJECT に CUSTOMER_A がセットされる
# → 環境変数を参照するツールはこれを使ってBacklogにアクセス# 書き込みが必要になった → 切り替え
bswitch switch customer-a-write
# → 自動で reader が除名され、writer が参加する(排他動作)
# → BACKLOG_API_KEY が writer のキーに変わる# 作業終了 → 参加中のプロジェクトから仮想ユーザーを除名
bswitch release
# → BACKLOG_API_KEY 等がunsetされる# どのプロファイル使うか迷ったら → 対話UIで選べる
bswitch # 引数なしで実行すると対話選択モード(bswitch switch と同じ)
# 期限付き(2時間後に自動解除対象になる)
bswitch switch customer-a-read --duration 2h
# 今の状態を確認
bswitch status
# 期限切れの付与を手動で解除
bswitch enforceswitchするたびに前のプロファイルは自動で解除されます(排他動作)。手動でreleaseしてからswitchする必要はありません。releaseは「参加中の付与を外す」コマンドです。作業終了時に使います。state.jsonが消えた等で残留参加が疑われる場合はbswitch release --allでconfig定義済みの全プロジェクトを走査して回収できます。- シェル統合(
eval "$(command bswitch shell-init zsh)")を入れないと、BACKLOG_API_KEY等の環境変数は実際にはセットされません(stdoutに出力されるだけです)。 --durationを指定しなくても、configにdefault_duration = 8hと書いておけば自動で期限付きになります。
仮想ユーザーを対象プロジェクトへ参加させます(排他動作: 先に前回付与分(state.jsonに記録されたプロジェクト)から仮想ユーザー2人を除名してから、選択分を参加させます)。
引数なしの bswitch は bswitch switch(対話選択モード)のショートカットとして動作します。
# プロファイル名を直接指定
bswitch switch customer-a
# 複数プロファイルを同時付与(read系とwrite/admin系の混在は不可。write+adminの混在は可)
bswitch switch customer-b customer-c
# 引数なし: インタラクティブ選択UI(fuzzy filter付き単一選択)
bswitch # ショートカット
bswitch switch # 同じ動作
# --multi: チェックボックス式の複数選択UI
bswitch switch --multi
# 有効期限付き付与(30s/30m/2h/8h形式)
bswitch switch customer-a --duration 2h複数プロファイルを選択した場合、BACKLOG_PROJECT はセットされません(unset)。単一プロジェクトに決め打ちすると、外部ツールが誤ったプロジェクトをデフォルト扱いする事故につながるためです。
state.jsonに記録された参加中プロジェクトから両仮想ユーザーを除名し、環境変数をunsetします。付与記録が0件ならAPIは呼びません。
--all を付けると、config定義済みの全プロジェクトを走査して除名します。state.json消失等で通常のreleaseから漏れた残留参加を回収する回復経路です(プロジェクト数×4回の更新系API呼び出しを消費するため、通常は不要です)。
bswitch release # 参加中プロジェクトのみ(通常はこちら)
bswitch release --all # config全プロジェクトを走査(回復用)各プロファイルのプロジェクトについて、仮想ユーザーの参加状況・権限・有効期限を表示します。
bswitch statusconfigのプロファイル一覧(プロファイル名・プロジェクトキー・権限)を表示します。API呼び出しは行いません。
bswitch list期限切れの付与だけを解除します(定期実行用)。解除対象がなければ何もせず終了コード0です。他の全サブコマンドも冒頭で同じ処理(遅延強制)を自動的に行うため、通常は明示的に呼ぶ必要はありません。定期実行のセットアップは「6. launchdによるenforce定期実行の手動セットアップ手順」を参照してください。
bswitch enforceシェル統合用の関数定義を出力します(.zshrc にeval経由で読み込む)。詳細は次章。
-
1Password CLI(
op)をインストールし、ログインしておきます(op signin)。 -
writer/reader それぞれの仮想ユーザーAPIキーを1Passwordのボールト(例:
MyVault)に保存します。op item create --category="API Credential" \ --vault MyVault \ --title "backlog-svc-writer" \ credential="<writerのAPIキー>" op item create --category="API Credential" \ --vault MyVault \ --title "backlog-svc-reader" \ credential="<readerのAPIキー>"
-
configの
writer_api_key_ref/reader_api_key_refにop://参照を書きます。writer_api_key_ref = op://MyVault/backlog-svc-writer/credential reader_api_key_ref = op://MyVault/backlog-svc-reader/credential
-
動作確認:
op read op://MyVault/backlog-svc-writer/credential値が表示されればOKです。bswitch実行時は
subprocess.run(["op", "read", ref])で都度取得され、ファイルには一切書き出されません。
環境変数 BACKLOG_WRITER_API_KEY / BACKLOG_READER_API_KEY が設定されていれば、op read を呼ばずそちらを優先して使います(E2Eテスト等で使用)。
Grantedと同様、子プロセスは親シェルの環境変数を変更できないため、シェル関数ラッパー方式で実現しています。
-
.zshrcに以下を追記します。eval "$(command bswitch shell-init zsh)"
-
シェルを再読み込みします。
source ~/.zshrc
-
これで
bswitchはシェル関数として定義されます。bswitch switch <profile>を実行すると、内部でcommand bswitch "$@"を実行して標準出力(export/unset行のみ)をevalし、現在のシェルに環境変数が反映されます。
--duration 付きで付与した権限は、どのサブコマンドを実行しても冒頭で自動的に期限切れチェック(遅延強制)が働きますが、しばらくbswitchを実行しない場合に備えて bswitch enforce を定期実行しておくと安全です。自動インストールは提供しないため、以下の手順で手動セットアップしてください。
重要: BSWITCH_MASTER_API_KEY をplist等のファイルに直接書かないでください。plistから起動するラッパースクリプトが、実行時にmacOS KeychainまたはFile 1Password CLI経由でキーを取得する構成にします。
-
マスターAPIキーをKeychainに登録する(一度だけ、手動で実行)。
security add-generic-password -a "$USER" -s bswitch-master-api-key -w "<マスターAPIキー>"
-
ラッパースクリプトを作成する(
~/.local/bin/bswitch-enforce.sh)。#!/bin/zsh set -euo pipefail export BSWITCH_MASTER_API_KEY="$(security find-generic-password -a "$USER" -s bswitch-master-api-key -w)" exec uv run --project /path/to/backlog-switcher bswitch enforce
chmod +x ~/.local/bin/bswitch-enforce.sh -
launchd用plistを作成する(
~/Library/LaunchAgents/com.example.bswitch-enforce.plist)。APIキーの値はplistに一切含めません。<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.example.bswitch-enforce</string> <key>ProgramArguments</key> <array> <string>/Users/<username>/.local/bin/bswitch-enforce.sh</string> </array> <key>StartInterval</key> <integer>300</integer> <key>StandardOutPath</key> <string>/tmp/bswitch-enforce.log</string> <key>StandardErrorPath</key> <string>/tmp/bswitch-enforce.log</string> </dict> </plist>
-
読み込んで有効化する。
launchctl load ~/Library/LaunchAgents/com.example.bswitch-enforce.plist -
停止・削除する場合。
launchctl unload ~/Library/LaunchAgents/com.example.bswitch-enforce.plist
ラッパースクリプトを以下のように変更するだけで、Keychainの代わりに1Password CLIからマスターキーを取得できます(1Password CLIがデスクトップアプリ連携でロック解除済みであることが前提)。
#!/bin/zsh
set -euo pipefail
export BSWITCH_MASTER_API_KEY="$(op read op://MyVault/backlog-master-api-key/credential)"
exec uv run --project /path/to/backlog-switcher bswitch enforceplistの設定手順は方式Aと同じです(ProgramArguments が指すスクリプトの中身だけが異なります)。
bswitch check の結果を Claude Code のステータスライン・tmux・starship などに表示するサンプルスクリプトを docs/examples/bswitch-statusline.js に用意しています。
🔀 customer-a(write) # プロファイルと権限が表示される
🔀 キー不一致 # BACKLOG_API_KEY が期待値と不一致
🔀 NOT_SET # BACKLOG_API_KEY 未設定
(何も出力しない) # 付与なし、または bswitch 未インストール
Claude Code の statusline.js・tmux・starship などから node bswitch-statusline.js を呼び出して使います。スクリプト全文は docs/examples/bswitch-statusline.js を参照してください。
uv sync
uv run ruff check .
uv run ruff format --check .
uv run pytest # 単体テスト(実APIは呼ばない)
BSWITCH_E2E=1 BSWITCH_E2E_SPACE=... BSWITCH_MASTER_API_KEY=... uv run pytest tests/e2e/