-
以下のアプリケーションをインストール&一度起動しておく
-
以下のZshコマンドを順番に実行
$git clone git@github.com:nrhrhysd616/dotfiles.git $chmod 744 dotfiles/init-mac.zsh $zsh dotfiles/init-mac.zsh
-
iTerm2の設定を反映
iTerm2を起動しメニューの「Settings...」から以下を設定
General -> Settings -> Import All Settings And Data...を選択
dotfiles/iterm2/iTerm2-all-settings.itermexportのファイルを選択してインポート -
.gitconfig.user.localファイルを編集- name: アルファベットフルネーム
- email: Githubで利用しているメールアドレス
user.signingkeyとgpg.ssh.programは端末ごとに絶対パスが変わるため、 次の手順のgen-mac-git-signkeyが書き込む(手で設定しない) -
コミット署名用の鍵をSecure Enclaveに用意する
init-mac.zshが既に一度実行しているが、手順4でメールアドレスを設定した後に もう一度実行して、GitHubへの鍵登録と署名者リストへの追記まで済ませるgen-mac-git-signkey
ghのトークンに鍵登録用のスコープが無い場合は案内が出るので、追加してから再実行するgh auth refresh -h github.com -s admin:ssh_signing_key,admin:public_key gen-mac-git-signkey
この端末の公開鍵が
git/allowed_signersへ追記されるので、commitしてpushする
そうすると他の端末でもこの端末が作った署名を検証できるようになる仕組みと運用は後述のコミット署名とSSH認証(Secure Enclave)を参照
-
sshdを再起動して設定を反映する
sudo launchctl stop com.openssh.sshd sudo launchctl start com.openssh.sshd
-
VSCodeのClineのCustom Instructionsを手動で設定する
現状設定のエクスポートなどができず、
settings.jsonファイルでの外出しも不可能なため手動 -
Tailscaleにサインインし、tailnetのHTTPS証明書を有効化する
後述の
csctlコマンドを使う場合のみ必要tailscale up
サインイン後、管理コンソールのDNS設定で 「HTTPS Certificates」をEnable HTTPSにする
これを有効にしないとtailscale certが証明書を発行できず、csctlが起動に失敗するサインインさえ済ませておけば接続は切っておいてよい
csctlが必要なときだけ自動で接続し、使い終わったら自動で切断する -
AWS CLIの認証設定
aws/configからコピーされた~/.aws/configを編集し、login_sessionにIAMユーザーのARNを設定する[default] login_session = arn:aws:iam::<account-id>:user/<iam-user-name> region = ap-northeast-1
認証は
aws loginで行う(AWS CLI 2.32.0以降が必要)
ブラウザでコンソールにサインインすると一時クレデンシャルが~/.aws/login/cacheに発行される
有効期限は最長12時間で、その間15分ごとに自動更新されるaws login aws sts get-caller-identity # ARNを確認この方式では長期のアクセスキーを一切作らないため、
~/.aws/credentialsは存在しないサインインにはMFAを設定したIAMユーザーを使うこと
ルートユーザーはアカウント解約・支払い情報の変更などルート専用の操作にのみ使い、 日常の操作やCLIからは使わない
コミット署名とGitHubへのSSH認証には、macOSのSecure Enclave内に作った鍵を使います。
秘密鍵はハードウェアから取り出せず、sc_auth create-ctk-identity -t noneで作るため
署名のたびに生体認証を求められません。コミット1回あたり0.1秒程度で、
Claude等のコーディングエージェントに無人で作業させても承認待ちで止まりません。
| 要素 | 役割 |
|---|---|
bin/gen-mac-git-signkey |
鍵の生成、GitHubへの登録、gitconfigへの反映、状態確認 |
bin/mac-ssh-keygen |
gitが署名時に呼ぶssh-keygenラッパー(Secure Enclaveのプロバイダを渡す) |
git/allowed_signers |
全端末の公開鍵を集めた署名者リスト(git log --show-signature用) |
ssh/config |
GitHubへの認証にSecure Enclaveの鍵を使う設定 |
~/.ssh/id_git_sign |
ハードウェア内の鍵を指すハンドル(秘密鍵そのものではない) |
-
鍵は端末ごとに別になる
Secure Enclaveの秘密鍵はエクスポートできないため、
~/.ssh/id_git_signを他の端末へ コピーしても機能しません。端末ごとに鍵を作り、GitHubには端末ごとに署名キーと 認証キーを登録します。鍵のパスとgitconfigの構造は全端末で同じなので、 運用上の違いは出ません -
状態を確認する
gen-mac-git-signkey --status
鍵、証明書の残り有効期間、gitconfigの設定値、GitHubへの登録状況、署名テストの結果が出ます
-
鍵を作り直す
gen-mac-git-signkey --recreate
sc_authが発行する自己署名証明書の有効期間は1年しかないため、期限が近づいたら 作り直します(--statusが残り90日を切ると警告します)作り直したあとも、GitHub上の古い鍵と
git/allowed_signersの古い行は消さないこと
消すとその鍵で署名した過去のコミットがUnverifiedになります -
1Passwordの署名に戻す
旧鍵と
op-ssh-signは残してあるので、~/.gitconfig.user.localと~/.ssh/configを戻せば復帰できますgit config --file ~/.gitconfig.user.local gpg.ssh.program "/Applications/1Password.app/Contents/MacOS/op-ssh-sign" git config --file ~/.gitconfig.user.local user.signingkey "<1Passwordに入れている公開鍵の文字列>" # ~/.ssh/config は init-mac.zsh が作ったバックアップ(config.bak.<timestamp>)から復元する
-
VSCodeなどGUIから操作したときに出るプロンプトについて
FIDO鍵は署名・認証の直前に
Confirm user presence for key ...という通知を出します。 標準エラーが端末ならそこへ1行出るだけですが、端末でない場合OpenSSHはSSH_ASKPASSの プログラムを起動してこれを表示しようとします。VSCodeやcode-serverはSSH_ASKPASSを 自前の入力ボックスに設定しているため、答える必要のない通知が毎回プロンプトとして現れます (VSCodeがメッセージをパスワード要求として解釈するので"key" has fingerprint ""のような 崩れた表示になります)-t noneで作った鍵はSecure Enclaveが即座に応答するので確認する相手はいません。git/.gitconfigのcore.sshCommand(fetch/push用)とbin/mac-ssh-keygen(署名用)の 両方でSSH_ASKPASS_REQUIRE=neverを設定して抑止しています。 手動のsshコマンドには影響しないため、新しいホストの鍵確認は従来どおりできます -
セキュリティ上の前提
秘密鍵は取り出せませんが、
-t noneで作った鍵はその端末で動くプロセスなら誰でも署名に使えます。 端末そのものが乗っ取られた場合は署名を偽造されうるので、そこは端末の安全性に依存します
gitleaksで、APIキーやトークンがコミットに混ざるのを機械的に止めています。
.gitignoreやClaude Codeのdeny設定が防げるのは「特定のファイル名・パス」だけで、
ソースコードにベタ書きされた値の中身までは見ていません。gitleaksがその穴を埋めます。
前述のとおり署名鍵は-t noneで作ってあり、コーディングエージェントが無人でコミット・pushできます。
人間のレビューを挟まずに履歴が公開へ流れる経路がある以上、検査は仕組みとして持たせておく必要があります。
検査は2箇所で走ります。
| どこで | 何を見るか | すり抜けられるか |
|---|---|---|
| ローカルのpre-commitフック | ステージ済みの差分 | --no-verifyで飛ばせる |
GitHub Actions(.github/workflows/gitleaks.yml) |
プルリクエストの差分 | 飛ばせない |
CIをプルリクエストだけに絞っているのは、pushでも走らせると同じコミットを二度検査することになり、 かといってmainへのpushに絞ると検出がマージ後になって遅いためです。マージ前に止めるのが目的なので、 mainへ直接pushした場合はCIが走りません(その場合の防壁はローカルのフックだけになります)。
フックの実体はgit/hooks/pre-commitで、init-mac.zshが~/.git-hooksへ
ディレクトリごとシンボリックリンクを張ります。git/.gitconfigのcore.hooksPathが
そこを指しているため、dotfilesに限らずこのマシンの全ローカルリポジトリで
コミット前の検査が走ります。
core.hooksPathはpathnameとして扱われるため~/が展開されます
(gpg.ssh.programは展開されず絶対パスが要る。同じgitconfigでも扱いが違う点に注意)。
実際の解決先は次のコマンドで確認できます。
git rev-parse --git-path hooks
# /Users/<user>/.git-hooks効かないケースが2つあります。
- 各リポジトリの
.git/hooksは無効化されます。core.hooksPathは探索先を丸ごと差し替えるためです - husky・lefthookを使うプロジェクトでは走りません。これらはリポジトリ側で
core.hooksPathを 設定するので、local設定がglobal設定に優先します。そこでの最後の砦はそのプロジェクトのCIになります
gitleaksが入っていない端末では、警告だけ出してコミットは通します。 dotfilesを適用していないマシンでコミットが一切できなくなるのを避けるためです。
既存の端末ではinit-mac.zshを再実行してください。 gitleaks本体のインストールと
~/.git-hooksのリンクはそこで行われます。git/.gitconfigはシンボリックリンクなので
pullした時点でcore.hooksPathは効きますが、リンク先が無い間はフックが存在しない扱いになり、
警告も出ないまま検査だけが走らない状態になります。次のコマンドで確認できます。
ls -l ~/.git-hooks && gitleaks versionコミットが中断され、検出したファイル・行・ルールIDが表示されます。
値そのものは--redactでマスクされるため、端末やログに秘密は残りません。
Finding: token = REDACTED
RuleID: github-pat
File: example.txt
Line: 1
Fingerprint: example.txt:github-pat:1本物のシークレットなら、混入した箇所を消してからコミットし直します。 誤検知だった場合の逃がし方は3つあります。
| 方法 | 書き方 | 効く範囲 |
|---|---|---|
| インラインコメント | 検出された行の末尾にgitleaks:allow |
その1行 |
.gitleaksignore |
表示されたFingerprintの値を1行書く |
その検出1件 |
.gitleaks.toml |
[[rules.allowlists]]にパスや正規表現を書く |
パターン全体 |
いずれもリポジトリのルートに置き、CIとローカルの両方で効きます。
Claude CodeとCodexに、同じルール・ナレッジ・スキルを全端末で共有させています。
実体はagents/配下の1箇所だけで、init-mac.zshが両エージェントの探索先へシンボリックリンクを張ります。
用途によって置き場を分けます。判断基準は「毎セッション必ず効かせたいか」です。
| 置き場 | 何を置くか | 読み込まれ方 |
|---|---|---|
agents/rules/ |
短い恒久的な行動ルール | 毎セッション全文 |
agents/knowledge/ |
参照用の長いナレッジ | 索引だけ。本文は必要になったときだけ読まれる |
agents/skills/ |
手順・ワークフロー | 名前と説明だけ。本文は起動時 |
rules/は全文が毎回コンテキストに乗るので、長い解説やたまにしか要らない知識はknowledge/へ置きます。
索引に書く1行には「いつ読むか」を必ず含めます。書名だけでは呼び出されません。
このリポジトリはpublicです。 業務・個人固有のナレッジは別のprivateリポジトリに置きます。
| 層 | 実体 | 端末間同期 |
|---|---|---|
| shared | このリポジトリ | される |
| private | agent-knowledge-private(privateリポジトリ) |
される |
| local | ~/.agents/knowledge-local/ |
されない |
local層をリポジトリの外に置いているのは、publicリポジトリでgit clean -xdfを打ったときに
業務ナレッジを巻き添えで消さないためです。
スキルは2つの層のものを1つの並びに混ぜるため、ディレクトリ全体ではなくスキルごとに
~/.claude/skills/<name>と~/.agents/skills/<name>へリンクします。
新しい端末ではinit-mac.zshがagent-knowledge-privateをghq getしますが、
アクセスできない場合は警告を出してprivate層を空のままにし、初期化は続行します。
エージェントに「これを覚えて」「今後は〜して」と頼むと、knowledgeスキルが起動して
層と置き場を判定し、索引への追記まで行います。sharedへ書く前に組織名・ホスト名・
認証情報が混ざっていないか確認する手順が入っています。
変更をpushしないと他の端末には反映されません。
スキルは形式が同一なので実体を共有できますが、ルールの自動ロードは仕組みが異なります。
| Claude Code | Codex | |
|---|---|---|
| スキル置き場 | ~/.claude/skills/ |
~/.agents/skills/ |
| グローバル指示 | ~/.claude/CLAUDE.md + ~/.claude/rules/**/*.md |
~/.codex/AGENTS.md 1ファイルのみ |
| ファイル分割 | @pathによるimport |
できない |
Codexはimport機構を持たないため、AGENTS.mdには「置き場の地図」だけを書き、
ルール本文はコマンドを明示してその場で読ませています。
Claude Codeがrulesを自動ロードするのに対し、Codexでは指示に留まるぶん遵守率は落ちます。
miseは複数の言語のバージョン管理ツールです。Python、Node.js、pnpm、Bun、Go、Ruby、Terraformをmiseで管理しています。
-
インストール済みツールの確認
mise list
-
利用可能なバージョンの確認
mise ls-remote python mise ls-remote node
-
グローバルバージョンの変更
# 特定バージョンを指定 mise use -g python@3.12 mise use -g node@20 # 最新版にアップデート mise use -g python@latest mise use -g node@lts
-
プロジェクトごとのバージョン指定
# プロジェクトディレクトリで実行 cd /path/to/project mise use python@3.11 # mise.tomlが生成される mise use ruby@3.3 # プロジェクトのRubyを固定したい場合
グローバルは
latestで運用しているため、バージョンを固定したいプロジェクトはこの方法で指定する -
rbenv・nodenv由来のバージョンファイルについて
miseは
.ruby-versionや.node-versionをデフォルトでは読まない
これらが置いてあるプロジェクトでもグローバルのバージョンが使われるため、 尊重させたい場合はツールごとに有効化するmise settings add idiomatic_version_file_enable_tools ruby
-
現在のバージョン確認
python --version node --version bun --version
CocoaPodsはグローバルにインストールせず、プロジェクトのGemfileでバージョンを固定します。
プロジェクトごとにpodのバージョンがズレても衝突せず、sudo gem installも不要になるためです
(init-mac.zshが用意するのはmise管理のRubyまで)。
-
Expoプロジェクト(
expo run:ios)の場合ルートに
Gemfileを置くだけでよく、podコマンドを自分で叩く必要はありませんcd /path/to/expo-project bundle init # Gemfileを生成 bundle add cocoapods # GemfileとGemfile.lockにバージョンが記録される npx expo run:ios # prebuildでios/を生成し、pod installまで自動実行される
Expo CLIはgitルートまでの親ディレクトリから
Gemfileを探し、gem "cocoapods"の記述があってbundle exec pod --versionが通る場合にbundle exec pod installを使います
Gemfileが無い場合はPATH上のpodを探し、見つからなければsudo gem install cocoapodsを促してきますPodfileはexpo prebuildがios/の中に生成するためpod initは不要です
プロジェクトルートでpod installを実行するとNo 'Podfile' foundになります -
Podfileを自分で管理するプロジェクトの場合
cd /path/to/ios-project bundle init bundle add cocoapods bundle exec pod init # Podfileを生成(.xcodeprojと同じ階層で実行) bundle exec pod install
Gemfile・Gemfile.lock・Podfile・Podfile.lockをコミットする -
既にGemfileがあるプロジェクト
bundle install # Gemfile.lockのバージョンでCocoaPodsが入る bundle exec pod install
-
gemの実体もプロジェクト配下に隔離する場合
上記だけでもバージョンは
Gemfile.lockで固定されますが、gem自体はmise管理Rubyの共有gemディレクトリに入ります
完全に分けたい場合は次のように設定しますbundle config set --local path vendor/bundle # .bundle/configが生成される bundle install echo "vendor/bundle" >> .gitignore
.bundle/configはコミットしてよい(チーム全員が同じ配置になる)
注意:
- 手で
podを叩くときはbundle execを付ける。グローバルにpodを入れていないため、 素のpod installはcommand not foundになる(バージョンを取り違えないための安全弁) - Rubyのバージョンを固定したい場合は
mise use ruby@3.3でmise.tomlを生成する
miseは.ruby-versionをデフォルトでは読まない(前述のmiseの使い方を参照) - 指定したバージョンにprecompiledバイナリが無いと、miseがソースビルドを行うため数分かかる
ビルドに必要なopenssl@3とlibyamlはinit-mac.zshが導入済み
copyはファイルや標準入力の内容をクリップボードへ入れるコマンドです。
pbcopy < README.mdのようにリダイレクトを書き分けなくても、copy README.mdとパスを渡すだけで済みます。
クリップボードへの書き込みには環境に応じたコマンドが選ばれます(macOSはpbcopy、Waylandはwl-copy、
X11はxclipかxsel、WSLはclip.exe)。特定のOS専用ではないためmac-のようなプレフィックスは付いていません。
-
ファイルをコピーする
copy README.md # 1つのファイル copy src/a.ts src/b.ts # 複数指定すると連結される copy -H src/*.ts # 各ファイルの前に "==> src/a.ts <==" の見出しを付ける
複数のコードをAIやチャットへ貼るときは
-Hを付けると、貼った先でもファイルの境目が残ります。 -
コマンドの出力をコピーする
git diff | copy # 標準入力から読む copy - README.md < head.txt # "-" は標準入力を表す。ファイルと混ぜられる
-
その他
copy -q README.md # 完了行を出さない copy --help # 使い方
コピーすると
✓ copied 42 lines, 1.2 KB from README.mdのように結果が1行出ます。 この表示もエラーもすべて標準エラーへ出るため、標準出力を使うパイプは汚れません。
補足:
- 指定したパスはすべて検査してから書き込みます。
copy a.txt typo.txtのように一部が読めない場合は 何もコピーせずに終了するので、その時点のクリップボードは壊れません - 空の入力は拒否します。 差分の無い
git diff | copyでクリップボードが黙って空になり、 直前にコピーした内容を失うのを避けるためです(pbcopyはこの場合クリップボードを空にします) - 貼り付け側のコマンドは用意していません。
pasteはPOSIX標準コマンド(/usr/bin/paste、複数ファイルの行を 横に連結する)として実在し、$HOME/.local/binがPATHの先頭にあるため、同名を置くと標準のpasteを隠します。 貼り付けはpbpasteを使ってください
csctlは任意のプロジェクトディレクトリをcode-server(ブラウザ版VS Code)として起動し、
Tailscale経由で公開するコマンドです。iPadなど同じtailnetに参加している端末のブラウザからプロジェクトを開けます。
各インスタンスはこのマシンのTailscale IPに直接バインドし、tailscale certが発行した証明書でTLSを終端するため、
https://<MagicDNS名>:<ポート>/でアクセスできます。インターネットには一切公開されません。
-
起動して公開する
# ディレクトリを指定するだけ(ポートは18080から空きを自動選択、インスタンス名はディレクトリ名) csctl start ~/Documents/Project/github.com/nrhrhysd616/dotfiles # カレントディレクトリを公開する csctl start # ポートと名前を明示する(複数プロジェクトを同時に公開できる) csctl start ~/work/api --port 18090 --name api
起動に成功するとURLとパスワードが表示されます。パスワードは全インスタンス共通で、初回に自動生成されます。
-
一覧・状態・ログ
csctl list # 全インスタンスの一覧 csctl status api # 1つの詳細(URL、PID、ログの場所など) csctl logs -f api # ログを追尾 csctl url api # URLだけを出力 csctl open api # ローカルのブラウザで開く
-
停止
csctl stop api # 名前で停止 csctl stop 18090 # ポートで停止 csctl stop all # 全部停止 csctl restart api # 同じディレクトリ・ポートで再起動
-
その他
csctl doctor # 依存コマンドとTailscaleの状態を確認 csctl password # 共通パスワードを表示 csctl password --reset # パスワードを再生成(再起動するまで既存インスタンスは旧パスワード) csctl cert # TLS証明書の発行・更新 csctl font install # Fira Codeをcode-server自身から配信する(後述) csctl help # 全コマンドのヘルプ
-
エディタ設定とテーマ
設定は
code-server/settings.jsonで管理し、csctlが各インスタンスのUser/settings.jsonからシンボリックリンクを張るため全インスタンスで共有される。 code-server上で設定を変更するとリポジトリ側のファイルが直接書き換わる。vscode/settings.json(デスクトップ版)とは別ファイルにしている。ブラウザ版では 拡張機能を入れ直す必要があり、外部アプリ連携も動かないため、 拡張機能に依存しない設定だけを持たせている。- テーマは組み込みの
Monokaiとアイコンテーマvs-setiを使う(拡張機能不要) - Monokai Proを使いたい場合はcode-server上で拡張機能
monokai.theme-monokai-pro-vscodeを インストールし、code-server/settings.jsonのworkbench.colorThemeを書き換える - 拡張機能は
~/.local/share/csctl/extensionsに置かれ全インスタンスで共有されるため、 インストールは一度だけでよい
- テーマは組み込みの
-
Fira Codeを全端末で使う
editor.fontFamilyはブラウザが動いている端末のフォントを参照するため、 Fira Codeが入っていないiPadなどでは効かずフォールバックされる。 以下を実行するとcode-server自身がWebフォントとして配信するようになり、どの端末でも適用される。csctl font install # 有効化(実行後、起動中のインスタンスはrestartが必要) csctl font status # 状態確認 csctl font remove # 元に戻す
この処理はcode-server本体のCSSに
@font-faceを追記し、~/Library/FontsのFira Codeを 配信ディレクトリへコピーする。code-serverを更新すると失われるが、 一度有効にしておけば次回のcsctl start時に自動で貼り直される。 -
動作のカスタマイズ
環境変数 説明 CSCTL_PORT_STARTポート自動選択の開始番号(既定: 18080、そこから20個スキャンする) CSCTL_AUTO_UP0を指定すると自動tailscale upを無効化するCSCTL_AUTO_DOWN0を指定すると自動tailscale downを無効化するCSCTL_CODE_SERVER_BINcode-serverのパスを明示する CSCTL_TAILSCALE_BINtailscaleのパスを明示する
Tailscaleの自動接続について:
- Tailscaleを常駐させる必要はありません。
csctl startが未接続(Stopped)を検出すると自動でtailscale upし、 最後のインスタンスを停止した時点で自動的にtailscale downします - 自動切断はcsctlが接続した場合のみ行われます。自分で
tailscale upした接続はcsctlが切ることはありません - サインインが必要な状態(NeedsLogin)ではブラウザ操作が要るため自動接続しません。手動で
tailscale upしてください
補足:
- 証明書は起動のたびに有効期限をチェックし、残り30日を切っていれば自動で更新されます
- 拡張機能と
settings.jsonは全インスタンスで共有されますが、開いているタブやウィンドウ状態などの ユーザーデータはインスタンスごとに分離されます(VS Codeは同じuser-data-dirを複数のサーバーで同時に使えないため) - Macがスリープするとインスタンスも応答しなくなります。常時アクセスしたい場合は
caffeinateなどで抑止してください - Macを再起動するとインスタンスは終了します(自動起動はしないため、再度
csctl startが必要)
-
iCloud
各種必要に応じて設定
-
アクセシビリティ > ディスプレイ
ポインタの枠線のカラー > 白 ポインタの塗りつぶしカラー > オレンジ
-
キーボード > キーボードショートカット…
スクリーンショット以外のショートカットをすべて削除
「F1、F2などのキーを標準のファンクションキーとして利用」はオン