複数の開発サーバーやコンテナに日常的にSSHし、ターミナルを何枚も開いて作業している方に向けた内容です。過去に打ったコマンドを探すのに苦労した経験がある方の参考になれば幸いです。
Bashの標準的な履歴管理はシンプルですが、セッションをまたいだ検索には向いていません。そこで登場するのが、SQLite(軽量な組み込み型データベースエンジン)にコマンド履歴を保存し、TUI(ターミナル上で動くグラフィカルな操作画面)で横断検索できるツール群です。Stinkpotはその中でも「Atuinの小型版」と紹介される、Go言語製の約400行という非常に小さい実装のツールです。
この種のツールは個人の生産性向上に直結する一方、業務用の開発端末や踏み台サーバーに導入する際は見落としがちな落とし穴があります。ここでは何が起きるか、なぜ起きるかを分解したうえで、自分の環境が該当するかの確認方法と対策手順を整理します。
何が起きるか:履歴の一元化がもたらす副作用
SQLiteバックエンドの履歴管理は、複数ターミナルの履歴を1つのデータベースファイルにまとめます。これによって検索性は上がりますが、同時に「1つのファイルに全コマンド履歴が集約される」という状態になります。
Stinkpotは同期機能を持たず、ローカル完結です。他のマシンに履歴を持ち出す設計にはなっていません。ただしローカルであっても、業務で扱った接続文字列やAPIキーをうっかりコマンドライン引数に含めていた場合、それが1つのSQLiteファイルに蓄積されることに変わりはありません。
もう1つ起きやすいのが、シェル起動時のオーバーヘッドです。.bashrcにeval "$(stinkpot init)"のような初期化コードを追加すると、対話シェルを開くたびにそのコードが実行されます。ここでエラーが出ると、ターミナルを開くたびにエラーメッセージが表示される、あるいは最悪の場合シェルが正常に起動しないという事態になりかねません。
なぜ起きるか:設計上の割り切りを理解する
原因を段階的に見ていきます。まず1点目は、Stinkpotが意図的に機能を絞っている点です。同期・AI補完・dotfiles管理・スクリプト機能・KVストア機能を持たず、Atuinのような多機能ツールとは異なる立ち位置を取っています。この割り切りが「約400行」という軽量さにつながっていますが、裏を返せば暗号化やマスキングといった安全機構も持たない可能性が高いということです。
2点目は、スキーマ(データベースの構造定義)が公開ドキュメント化されていない点です。公開されている性能上の工夫はタイムスタンプにインデックスを張り、逆順検索でカバリングインデックス(一時的なB-treeを使わずに済む索引)を使っている、という1点のみです。それ以外のスキーマ詳細は現時点のソースコードを確認しない限り内部実装として扱うべきものです。つまり「どのカラムに何が平文で入るか」を業務利用の前提で確認する作業は、利用者側の責任になります。
3点目は、シェル統合の仕組みそのものに起因します。stinkpot initが出力する内容をevalでそのままシェルに読み込ませる方式は、多くのシェル拡張ツールに共通するパターンです。この方式は初期化コードの中身を事前に見ずにevalすると、想定外のエイリアスやキーバインド上書きが起きても気づきにくいという弱点を持ちます。
自分のプロジェクトが該当するか確認する
以下の観点で、自分のチームや開発端末が該当するかを確認してください。
- 開発サーバーや踏み台サーバーに複数人がSSHでログインし、対話シェルを共有・切り替えて使っているか
- コマンドライン引数にパスワードやトークンを直接渡す運用が過去にあったか(
historyコマンドでgrep -i passwordやgrep -i tokenをかけて確認できます) .bashrcや.bash_profileを全社共通の初期化スクリプトとして配布・管理しているか- 監査ログやコンプライアンス要件でコマンド履歴の保存期間・保存場所が定められているか
これらに1つでも当てはまる場合、SQLiteバックエンドの履歴ツールを個人端末の延長で気軽に導入するのは慎重になったほうがよい状況です。
バージョンやビルド元の確認は、まずリポジトリのクローンから始めます。
git clone https://tangled.org/oppi.li/stinkpot
cd stinkpot
go build -o stinkpot .ビルドが通ることを確認したら、実行ファイルをすぐに.bashrcへ組み込まず、まず./stinkpot initを単体で実行し、出力されるシェルコードを目視で確認してください。中身を確認せずにevalするのは避けたほうが安全です。
対策の手順
導入する場合は、以下の順序で進めることをおすすめします。
1. 既存の.bashrcと現在のBash履歴ファイル(通常~/.bash_history)をバックアップします
2. go buildでビルドしたバイナリを、個人の検証用シェルだけに限定して先に試します(全社配布の初期化スクリプトにはまだ入れません)
3. stinkpot initの出力内容を確認し、意図しないキーバインド上書き(特にCtrl+Rによるリバース検索の乗っ取り)がないかを見ます
4. 問題なければ新しい対話シェルを開き、エラーなく起動すること、stinkpotコマンドがPATHから解決されることを確認します
5. stinkpot importで既存履歴を取り込む前に、history | grep -iE "password|token|secret|key"のようなコマンドで機微情報が含まれていないかをチェックします
6. 業務端末や共有サーバーへの展開は、上記の個人検証を経てからチーム内でレビューし判断します
ZshやFish、PowerShellを使っている場合は、公開されているのがBash向けの手順である点に注意してください。同じ初期化方法がそのまま動く保証はないため、別シェルへの流用は独自に検証する必要があります。
まとめ
SQLiteバックエンドのシェル履歴ツールは、複数セッションをまたいだコマンド検索を楽にしてくれる一方、履歴の一元化そのものが機微情報の集約点になり得ます。
導入前には、共有サーバーでの利用有無、過去のコマンド履歴に秘密情報が混じっていないかの棚卸し、.bashrcへの追記内容の目視確認、この3点を最低限やっておくと安心です。
まずは個人の検証環境でビルドし、stinkpot initの出力を確認するところから始めてみてください。そのうえで、既存履歴のインポート前にgrepで機微情報の混入をチェックする一手間が、後々の事故を防ぐ分かれ目になります。