Skip to content

Instantly share code, notes, and snippets.

@sasasin
Last active April 16, 2026 15:56
Show Gist options
  • Select an option

  • Save sasasin/96778b869ad14f692a8f4fb6349c6171 to your computer and use it in GitHub Desktop.

Select an option

Save sasasin/96778b869ad14f692a8f4fb6349c6171 to your computer and use it in GitHub Desktop.
rtk-ai/rtk を codex に読解してもらった。Rust なんもわからん
  • https://github.com/rtk-ai/rtk というやつ便利そうなので、公私ともに便利に使っていきたいぞ
  • ところで rtk って安心して安全に使えるものなのか気になった
  • Claude Code とか Codex が rtk ls とか rtk git したとき、コマンドラインとか、実コマンドの標準入出力を勝手に外部送信してたら困る
  • 私は Rust の読み書きはできないんだな
  • Codex くんに質問して読解してもらいつつ、見様見真似で Rust コード読解してみた。正直キツかった。だいぶわからなかった
  • ghq get して取れた今日時点の main の HEAD の a69935746172d913fcbd282d720d2daf5025e5e9 で読解してもらった
  • rtk は外部送信やってなさそうな気配を感じた
  • rtk がコマンド実行の統計データ保管する SQLite ファイルは、うっかり漏洩しないよう厳重保管として扱うのが安全そう
    • macOS なら "/Users/$USER/Library/Application Support/rtk/history.db" に転がってた
  • なんなら時々 SQLite ファイル削除するくらいでいいんじゃないかな
  • SQLite 保管を停止する機能は存在しない

RTK リポジトリ読解メモ 001

質問

このプロダクトのソースコードから、これが何をするプログラムなのか、フォルダ構成、プログラミング言語、利用しているライブラリ、アーキテクチャを把握して、 reading/001.md として文書化してほしい。

1. このプログラムは何をするものか

このプロダクトは RTK (Rust Token Killer) という CLI ツールで、LLM エージェントや開発者が実行する各種コマンドの出力を、意味を保ちながら圧縮・要約・整形してトークン消費を減らすためのプロキシです。

典型的には git statuscargo testpnpm, pytest, docker, aws, grep, read などの出力をそのまま LLM に渡す代わりに、RTK が間に入り、

  • ノイズ除去
  • エラー中心の抽出
  • 同種メッセージのグルーピング
  • 重複排除
  • 大量出力の切り詰め
  • JSON やログの構造化表示

を行います。

その結果、README やアーキテクチャ文書上では 60〜90% 程度のトークン削減を狙う設計になっています。

また、このツールは単なる手動 CLI ではなく、Claude Code / Codex / Cursor / Copilot / Windsurf などの AI コーディングエージェント向け hook システムを持っており、git status のような生コマンドを自動的に rtk git status に書き換える用途も担っています。

2. プロダクトの全体像

コードベースを読む限り、このリポジトリは次の 4 本柱で構成されています。

  1. CLI プロキシ本体 外部コマンドを実行し、その出力をフィルタして返す Rust バイナリ。

  2. コマンド書き換えシステム LLM エージェントの hook から呼ばれ、通常のコマンドを rtk ... へ変換する仕組み。

  3. トークン削減の記録と分析 実行結果を SQLite に保存し、rtk gain などで削減量を可視化する仕組み。

  4. フィルタ定義の拡張機構 Rust 実装の高機能フィルタに加えて、TOML ベースの宣言的フィルタも持ち、未対応コマンドもある程度カバーできる仕組み。

3. プログラミング言語

主言語は Rust です。src/ 以下に 99 個の .rs ファイルがあり、CLI 本体・ルーティング・フィルタ・hook 管理・分析機能の中核は Rust で実装されています。

補助的に次の言語も使われています。

  • TypeScript openclaw/scripts/benchmark/ に存在。OpenClaw 向けプラグインとベンチマーク支援コード。
  • Shell install.sh, scripts/*.sh, hooks/*/*.sh。インストール、テスト、hook 配布用。
  • TOML Cargo.tomlsrc/filters/*.toml。Rust パッケージ定義と組み込みフィルタ DSL。
  • Markdown README 群、ガイド、保守者向けドキュメントが非常に充実しています。

つまり、実行エンジンは Rust、周辺統合と配布は Shell/TypeScript、設定と簡易フィルタは TOML という構成です。

4. 利用している主なライブラリ

Cargo.toml から確認できる主要依存は次の通りです。

  • clap CLI 定義とサブコマンド解析。
  • anyhow エラーハンドリング。
  • regex, lazy_static 出力解析、ルールマッチ、コマンド書き換え判定。
  • serde, serde_json JSON 入出力の解析・整形。
  • rusqlite (bundled) トラッキング DB の永続化。SQLite を内包して配布しやすくしている。
  • toml 設定ファイルと TOML フィルタ DSL の読み書き。
  • chrono タイムスタンプ処理。
  • tempfile hook インストールや設定更新時の安全な一時ファイル操作。
  • sha2 hook 整合性検証用のハッシュ計算。
  • ureq 軽量 HTTP 通信。テレメトリやネットワーク取得系に利用される設計。
  • flate2 圧縮データ処理。
  • quick-xml XML 系出力の解析補助。
  • which 実行コマンドの検出。
  • ignore, walkdir, dirs ファイル探索やユーザーディレクトリ解決。
  • colored CLI 表示の色付け。
  • automod ディレクトリ単位で Rust モジュールを自動公開する補助。

Rust 依存の選び方からも、非同期サーバではなく 軽量・単一バイナリ・高速起動の CLI を重視していることが読み取れます。

5. フォルダ構成

リポジトリの重要ディレクトリは次の通りです。

ルート直下

  • src/ アプリケーション本体。
  • docs/ アーキテクチャ、利用方法、メンテナ向け資料。
  • hooks/ 各 AI ツール向けに配布する hook スクリプトやルール。
  • scripts/ テスト、ベンチマーク、インストール補助。
  • tests/fixtures/ パーサやフィルタの検証用フィクスチャ。
  • openclaw/ OpenClaw 向けプラグイン実装。TypeScript ベース。
  • Formula/ Homebrew 配布用 formula。
  • reading/ 今回のような読解メモ置き場。

src/ 配下

  • src/main.rs CLI エントリポイント。clap でサブコマンドを解釈し、各モジュールへルーティングする。
  • src/cmds/ 実際のコマンド実行と出力フィルタを担当する中心レイヤー。
  • src/core/ 設定、トラッキング、共通ユーティリティ、TOML フィルタ、表示補助などの基盤。
  • src/hooks/ rtk init, rtk rewrite, hook 整合性検証、trust 管理など。
  • src/discover/ コマンド書き換えルール判定と、過去履歴からの適用可能コマンド分析。
  • src/analytics/ SQLite に蓄積した削減実績の分析表示。
  • src/learn/ 失敗コマンドと修正コマンドの対を学習して、CLI ミスの傾向を検出。
  • src/parser/ JSON/構造化出力を段階的に解釈する共通パーサ基盤。
  • src/filters/ TOML DSL による宣言的フィルタ群。

src/cmds/ の内訳

このディレクトリはエコシステム別に整理されています。

  • git/ git, gh, gt, diff 系。
  • rust/ cargo, err, test など Rust 開発向け。
  • js/ npm, pnpm, vitest, tsc, next, prettier, playwright, prisma, lint 系。
  • python/ ruff, pytest, mypy, pip
  • go/ go, golangci-lint
  • ruby/ rake, rspec, rubocop
  • dotnet/ dotnet, trx, binlog, format report。
  • cloud/ aws, docker, kubectl, curl, wget, psql
  • system/ ls, tree, read, grep, find, json, log, env, deps, summary など汎用系。

この構成から、RTK は単一用途のツールではなく、開発現場で頻出する CLI 群の出力圧縮プラットフォームとして作られていると分かります。

6. アーキテクチャ

6.1 大枠

アーキテクチャは 「hook + コマンド分類 + 実行/フィルタ + トラッキング」 の 4 層です。

  1. Hook 層 AI エージェントが実行しようとする生コマンドを捕捉する。

  2. Rewrite / Discover 層 そのコマンドを RTK で処理できるか判定し、rtk git status のように書き換える。

  3. Command Filter 層 実コマンドを起動し、出力をコマンド固有ロジックで圧縮する。

  4. Core / Tracking 層 フィルタ結果を表示し、入出力文字量や削減率を SQLite に記録する。

6.2 実行フロー

標準的な流れは次の通りです。

  1. LLM エージェントやユーザーがコマンドを実行する。
  2. hook が rtk rewrite "<元コマンド>" を呼ぶ。
  3. src/discover/registry.rs がルールに基づいて書き換え可否を判定する。
  4. src/main.rsclaprtk のサブコマンドを解析する。
  5. 対応する src/cmds/... モジュールが実コマンドを実行する。
  6. フィルタ関数がノイズ除去・要約・集約を行う。
  7. src/core/tracking.rs が元出力と加工後出力を比較して DB に保存する。
  8. src/analytics/ の各コマンドがその記録を可視化する。

6.3 重要な設計方針

文書とコードから、次の設計方針が明確です。

  • 単一バイナリ ランタイム依存を抑え、配布しやすくしている。
  • 高速起動 開発ループで何度も呼ばれるため、起動オーバーヘッドを極小化している。
  • Fail-safe フィルタ失敗時は raw 出力へフォールバックする。
  • 終了コードの保持 もとのコマンドの exit code を極力壊さず返す。
  • 未知コマンドも通す 未対応コマンドは passthrough や TOML フィルタへ流す。
  • 構造化出力を優先 JSON/NDJSON/XML が取れるツールではそれを使って賢く圧縮する。

7. サブシステム別の理解

7.1 main.rs

main.rs は巨大な CLI ルータです。Commands enum に各サブコマンドを定義し、match で各実装モジュールへ渡します。つまりアプリケーションの中心はここですが、実際の知能は cmds, discover, core, hooks 側に分散しています。

7.2 cmds/

最も重要なレイヤーです。各モジュールは外部 CLI を Command::new(...) で実行し、その stdout/stderr をツール固有のルールで圧縮します。

ここには大きく 2 種類あります。

  • Rust フィルタ JSON 解析、ステートマシン、ルール別集約などの高度な処理。
  • 共有 runner に乗る実装 共通の実行・追跡・終了コード処理を core/runner.rs に委譲する実装。

7.3 core/

アプリ共通基盤です。特に重要なのは次です。

  • tracking.rs トークン削減記録の永続化。
  • runner.rs コマンド実行の共通フロー。
  • toml_filter.rs 宣言的フィルタ実行。
  • config.rs 設定読み込み。
  • tee.rs フィルタ失敗時やトランケーション時の救済出力。
  • utils.rs ANSI 除去、コマンド解決、トークン推定などの横断ユーティリティ。

7.4 hooks/

AI エージェントへの組み込みを担当します。単に shell script を置くだけでなく、

  • hook インストール
  • 設定ファイルの patch
  • SHA-256 による hook 改ざん検知
  • rewrite 用 CLI ブリッジ
  • trust / verify / audit

まで管理しており、運用レイヤーとしてかなり重要です。

7.5 discover/

このプロダクトの「自動書き換え頭脳」です。コマンドの字句解析、複合コマンド分解、rewrite ルール適用、履歴分析がここにあります。

特に &&, ||, |, redirect, quote を含むコマンドを安全に扱うため、単純な split ではなく lexer ベースで処理している点が特徴です。

7.6 analytics/

トラッキング DB を読み取って、どれだけトークンを削減したかをダッシュボードとして見せる層です。設計上は read-only で、記録は core/tracking、可視化は analytics と責務分離されています。

7.7 learn/

Claude Code のセッション履歴から、「誤った CLI 実行 → 修正コマンド」のパターンを検出して学習する機能です。これは単なる出力圧縮ツールから一歩進んで、エージェントの操作ミス改善支援まで視野に入れていることを示しています。

7.8 parser/

ツール出力を

  • Full parse
  • Degraded parse
  • Passthrough

の 3 段階で扱う共通インフラです。ツールバージョン差異やフォーマット崩れに強くするための設計で、堅牢性を高めています。

8. このコードベースから見える技術的特徴

強み

  • CLI 体験に特化した非常に明快な責務分離
  • フィルタ失敗時のフォールバックを前提にした安全設計
  • LLM エージェント統合まで含めた実運用志向
  • SQLite による計測基盤があり、効果を定量化できる
  • Rust 実装と TOML DSL 実装を併用し、拡張性と開発速度を両立している

性格づけ

このプロダクトは「単なるコマンドラッパー」ではなく、

  • AI エージェント向けの出力最適化ミドルウェア
  • CLI 出力圧縮フレームワーク
  • トークン節約の観測基盤

を兼ねたツールだと理解するのが適切です。

9. まとめ

このリポジトリは、Rust 製の高性能 CLI プロキシとして、

  • 開発系コマンドの出力を圧縮し
  • AI エージェントの hook と連携して自動利用させ
  • 削減効果を SQLite に蓄積・分析し
  • 必要に応じて TOML ベースのフィルタでも拡張できる

ように設計されています。

中心となるアーキテクチャは main.rs のルーティングを起点に、

  • src/cmds/ が各種コマンドの実行・圧縮、
  • src/core/ が共有基盤、
  • src/hooks/ がエージェント統合、
  • src/discover/ が rewrite 判定、
  • src/analytics/ が可視化、
  • src/learn/ が履歴学習

を分担する形です。

コードベース全体としては、「LLM に渡す CLI 出力を最適化するための Rust 製プラットフォーム」 という理解でほぼ間違いありません。

RTK セキュリティ読解メモ 002

質問

このプログラムを利用するにあたりセキュリティ監査的な観点で大丈夫なものか見極めたい。rtk コマンド経由で実プログラムを実行したとき、たとえば、「渡されたコマンドラインを、どこか外部サーバーに送信している」とか「コマンド実行結果を、どこか外部サーバーに実行している」ような箇所が存在しているか、ソースコード読解して調べてほしい。調べた結果を reading/002.md として保存してほしい。

1. 結論

ソースコードを読む限り、rtk実行したコマンドライン全文やコマンド実行結果本文を、常時どこかの外部サーバーへ送信する実装 は見当たりませんでした。

ただし、例外として telemetry(匿名利用統計) の送信機能は存在します。これは run_cli() の冒頭で毎回 maybe_ping() を呼ぶ実装になっていますが、実際に送信されるのは

  • ビルド時に telemetry URL が埋め込まれている
  • ユーザーが明示的に同意している
  • RTK_TELEMETRY_DISABLED=1 で無効化していない

という条件を満たした場合だけです。根拠: src/main.rs:1284-1286, src/core/telemetry.rs:20-66

送信内容もコード上は 集計済みメタデータ中心 で、少なくともこの実装では

  • コマンド出力本文
  • フルのコマンドライン引数
  • ソースコード本文

を telemetry payload に直接入れていません。根拠: src/core/telemetry.rs:106-150, src/core/tracking.rs:912-923, src/core/tracking.rs:960-1003

2. 外部送信がある箇所

2.1 telemetry ping

外部 HTTP 通信をしている本体コードは、調べた範囲では telemetry の 1 系統です。

  • src/core/telemetry.rs:14-15 RTK_TELEMETRY_URL / RTK_TELEMETRY_TOKENoption_env!() でビルド時注入
  • src/core/telemetry.rs:20-66 同意・設定・環境変数・23時間間隔を確認し、条件を満たす時だけ別スレッドで送信
  • src/core/telemetry.rs:106-150 ureq::post(url) で JSON を POST

送信 payload に含まれるのは、コード上は主に以下です。

  • device_hash
  • version, os, arch, install_method
  • 24時間のコマンド数
  • 上位コマンド名
  • 節約トークン数や節約率
  • passthrough 上位、parse failure 件数
  • hook 種別、設定有無、プロジェクト数
  • ecosystem の比率、meta command 使用回数

根拠: src/core/telemetry.rs:106-140

2.2 telemetry forget 時の erasure request

rtk telemetry forget 実行時には、サーバー側削除要求として別の POST もあります。

  • src/core/telemetry_cmd.rs:143-180

ここで送るのは

  • device_hash
  • action: "erasure"

のみです。

3. 「送っていない」と判断した根拠

3.1 実行結果本文は tracking に保存していない

rtk は多くの実行経路で標準出力・標準エラーをいったんメモリ上で受け取りますが、それをそのまま DB に保存しているわけではありません。

共通実行器では rawfilteredtimer.track(...) に渡していますが、Tracker::record() に保存されるのは

  • original_cmd
  • rtk_cmd
  • project_path
  • input_tokens
  • output_tokens
  • saved_tokens
  • savings_pct
  • exec_time_ms

です。出力本文そのものを格納するカラムはありません。根拠: src/core/runner.rs:68-121, src/core/tracking.rs:263-272, src/core/tracking.rs:351-385

rtk proxy でも子プロセスの stdout/stderr を取り込みますが、最終的に tracking に渡すだけで、保存先は同じ token 集計テーブルです。根拠: src/main.rs:2116-2210, src/core/tracking.rs:351-385

3.2 telemetry はコマンド名を短くして送っている

telemetry 用の集計では、フルコマンドではなく短縮した情報だけを使う意図がコードにあります。

  • top_commands()rtk_cmd から 2語目だけを抜いて command name にしている 根拠: src/core/tracking.rs:912-923
  • top_passthrough()original_cmd の先頭語だけを使う 根拠: src/core/tracking.rs:960-975
  • low_savings_commands()rtk_cmd を最大3語に切り詰める 根拠: src/core/tracking.rs:989-1003
  • ecosystem_mix() は command を category 化して比率だけ送る 根拠: src/core/tracking.rs:1076-1138

つまり、ローカル DB には比較的詳しい文字列が残る一方、telemetry payload はそれを集計・短縮してから送る実装です。

4. 注意すべき点

4.1 ローカル保存はかなりある

「外部送信は限定的」とはいえ、ローカル保存 はあります。

commands テーブルには

  • original_cmd
  • rtk_cmd
  • project_path

が保存されます。根拠: src/core/tracking.rs:263-289, src/core/tracking.rs:368-381

また parse_failures テーブルには

  • raw_command
  • error_message

が保存されます。根拠: src/core/tracking.rs:312-318, src/core/tracking.rs:401-418

そのため、

  • ローカル端末上の履歴 DB にフルコマンドが残る
  • parse failure の文脈によってはエラーメッセージに機微情報が混ざる可能性がある

という点は、外部送信とは別のローカル情報管理リスクとして認識しておくべきです。

4.2 telemetry の送信先 URL 自体はソースからは確定できない

送信先 URL は option_env!("RTK_TELEMETRY_URL") でビルド時埋め込みなので、このリポジトリのソースだけでは

  • 実際の送信先ホスト
  • 手元の配布バイナリに URL が埋め込まれているか

は断定できません。根拠: src/core/telemetry.rs:14, src/core/telemetry_cmd.rs:162-165

つまり、このソースコードから言えるのは「送信機構はあるが、送信先はビルド成果物依存」 までです。

4.3 rtk が起動する子コマンド自身の通信は別問題

rtkcurl, wget, aws, npm, pnpm など、もともとネットワーク通信を行う CLI もラップします。これらの通信は RTK が勝手に追加した送信ではなく、子コマンド本来の通信 です。

今回の監査対象である「rtk 自身が裏でコマンドラインや出力を外へ送るか」という点では、主に telemetry を見ればよい、という整理になります。

5. 監査観点での評価

現時点の評価は次の通りです。

  • NG と断じるほどの隠し送信実装は見当たらない
  • ただし opt-in telemetry 実装は存在する
  • telemetry payload はコード上、集計情報中心で、出力本文やフル引数は直接送っていない
  • 一方で ローカル SQLite にはフルコマンドや project path が保存される
  • 配布済みバイナリの最終挙動は、ビルド時埋め込みの telemetry URL 有無に依存する

したがって、セキュリティ監査的には

  1. ソースだけを見る限り、rtk は「常時コマンド内容や実行結果を外部送信するタイプ」ではない
  2. ただし telemetry を完全に嫌うなら、rtk telemetry disable または RTK_TELEMETRY_DISABLED=1 を使うのが安全
  3. さらに厳密に見るなら、利用中バイナリに RTK_TELEMETRY_URL が埋め込まれているかをバイナリ解析で確認するとよい

という結論です。

6. 補足: リポジトリ内ドキュメントとの整合

公式ドキュメント側も、telemetry は

  • デフォルト無効
  • 明示同意が必要
  • フルコマンドや引数、ファイルパスは送らない

と説明しています。根拠: docs/TELEMETRY.md:3, docs/TELEMETRY.md:24-27, docs/TELEMETRY.md:109-117, docs/TELEMETRY.md:119-133

今回のコード読解結果は、少なくとも主要実装についてはこの説明と大きく矛盾していません。

RTK SQLite / rtk gain 読解メモ 003

質問

このプログラムを利用するにあたり SQLite ファイルはどこに生成されるのか知りたい。セキュリティ監査観点で、この SQLite ファイルをどのように扱うことが望ましいか見極めたい。SQLite ファイル中身のデータベーススキーマ構成を知りたい。それぞれテーブルに、 rtk をどのように実行したとき、どのような情報が保管されるのか知りたい。rtk gain したとき、どのようなクエリで、どのように集計しているのか知りたい。以上をソースコード読解して調べてほしい。調べた結果を reading/003.md として保存してほしい。なお既にこのパソコン内に rtk コマンドはインストール済であり、手元で rtk ls などと実行した結果がある。SQLite ファイルの中身を覗き込んで調査の補助として使ってよい。

結論

  • SQLite ファイルの保存先は、優先順位つきで決まります。
      1. 環境変数 RTK_DB_PATH
      1. config.toml[tracking].database_path
      1. OS 既定の local data dir 配下の rtk/history.db
    • 根拠: src/core/tracking.rs:1156-1171, src/core/constants.rs:1-6
  • この macOS 環境では、実際に history.db/Users/${USER}/Library/Application Support/rtk/history.db にありました。rtk config が示した設定ファイルは /Users/${USER}/Library/Application Support/rtk/config.toml でした。
  • 追跡 DB には少なくとも commandsparse_failures の 2 テーブルがあります。
  • rtk gain は、この DB を読んで集計するだけで、集計 SQL の大半は src/core/tracking.rs にあります。表示整形は src/analytics/gain.rs です。
  • セキュリティ監査観点では、この DB は「出力本文」は持たない一方で、「実行コマンド文字列」「カレントディレクトリ由来の絶対パス」「parse error 文字列」は保存するため、ローカル機微情報の塊として扱うのが妥当です。

1. SQLite ファイルはどこに生成されるか

実装上の決定順序

get_db_path() は次の順で DB パスを決めています。

  1. RTK_DB_PATH
  2. config.tomltracking.database_path
  3. dirs::data_local_dir()/rtk/history.db

根拠:

  • src/core/tracking.rs:1156-1171
  • src/core/constants.rs:1-6
  • src/core/config.rs:34-49

つまり、デフォルト名は tracking.db ではなく history.db です。コードコメントや一部 docs には古い tracking.db 表記が残っていますが、現行コードの定数は HISTORY_DB = "history.db" です。

このマシンでの実測

  • rtk --versionrtk 0.36.0
  • rtk config/Users/${USER}/Library/Application Support/rtk/config.toml を表示
  • 同ディレクトリに history.db が存在

確認できたファイル:

/Users/${USER}/Library/Application Support/rtk/history.db

2. DB スキーマ

実 DB の .schema とコードの CREATE TABLE は一致していました。

commands

CREATE TABLE commands (
    id INTEGER PRIMARY KEY,
    timestamp TEXT NOT NULL,
    original_cmd TEXT NOT NULL,
    rtk_cmd TEXT NOT NULL,
    input_tokens INTEGER NOT NULL,
    output_tokens INTEGER NOT NULL,
    saved_tokens INTEGER NOT NULL,
    savings_pct REAL NOT NULL,
    exec_time_ms INTEGER DEFAULT 0,
    project_path TEXT DEFAULT ''
);
CREATE INDEX idx_timestamp ON commands(timestamp);
CREATE INDEX idx_project_path_timestamp ON commands(project_path, timestamp);

根拠:

  • src/core/tracking.rs:262-309
  • 実 DB .schema

parse_failures

CREATE TABLE parse_failures (
    id INTEGER PRIMARY KEY,
    timestamp TEXT NOT NULL,
    raw_command TEXT NOT NULL,
    error_message TEXT NOT NULL,
    fallback_succeeded INTEGER NOT NULL DEFAULT 0
);
CREATE INDEX idx_pf_timestamp ON parse_failures(timestamp);

根拠:

  • src/core/tracking.rs:311-323
  • 実 DB .schema

このマシンの実データ件数

実測では以下でした。

  • commands: 5 件
  • parse_failures: 1 件

3. 各テーブルに何が入るか

3.1 commands

いつ書かれるか

TimedExecution::track() または TimedExecution::track_passthrough() が呼ばれると、Tracker::record() に流れて commands へ INSERT されます。

根拠:

  • src/core/tracking.rs:1292-1305
  • src/core/tracking.rs:1328-1333
  • src/core/tracking.rs:351-385

この設計なので、各種 src/cmds/* でフィルタ成功時だけでなく、fallback や passthrough 系でも記録されます。main.rs の fallback 経路でも track() / track_passthrough() が呼ばれています。

根拠:

  • src/main.rs:1145-1151
  • src/main.rs:1173-1175
  • src/core/README.md:116-117

各カラムの意味

  • id
    • 連番の主キー
  • timestamp
    • Utc::now().to_rfc3339() で入る UTC 時刻
  • original_cmd
    • 素のコマンド文字列
    • 例: ls -la /path
  • rtk_cmd
    • RTK 側での表示用コマンド文字列
    • 例: rtk ls -la /path
  • project_path
    • 記録時点の current_dir().canonicalize() 結果
    • つまり実行時 cwd の絶対パス
  • input_tokens
    • 生出力文字列の概算 token 数
  • output_tokens
    • RTK 出力文字列の概算 token 数
  • saved_tokens
    • input_tokens - output_tokens
  • savings_pct
    • (saved_tokens / input_tokens) * 100
  • exec_time_ms
    • TimedExecution 開始からの経過ミリ秒

根拠:

  • src/core/tracking.rs:359-380
  • src/core/tracking.rs:1201-1222
  • src/core/tracking.rs:1292-1305

token 数はどう計算しているか

厳密 tokenizer ではなく、ceil(text.len() / 4.0) の近似です。

根拠:

  • src/core/tracking.rs:1201-1222

passthrough 時はどうなるか

track_passthrough()input_tokens=0, output_tokens=0 で保存します。したがって saved_tokens=0, savings_pct=0.0 です。

根拠:

  • src/core/tracking.rs:1328-1333

これは「出力を捕捉していない interactive/streaming 系コマンドでも、実行時間だけは残す」ための扱いです。

実 DB のサンプル

このマシンの commands には実際に次のような値が入っていました。

  • original_cmd: ls -la /Users/${USER}/tmp/pstree-mem/dist/
  • rtk_cmd: rtk ls -la /Users/${USER}/tmp/pstree-mem/dist/
  • project_path: /Users/${USER}/tmp/pstree-mem
  • input_tokens: 81
  • output_tokens: 22
  • saved_tokens: 59
  • savings_pct: 72.8395061728395
  • exec_time_ms: 4

ここから分かるのは、ファイル内容本文は入っていないが、実行対象パスや作業プロジェクトの絶対パスはそのまま入る ということです。

3.2 parse_failures

いつ書かれるか

Clap で想定外のサブコマンドや引数になって fallback 経路へ入ったとき、record_parse_failure_silent() 経由で parse_failures に書かれます。

根拠:

  • src/main.rs:1076-1180
  • src/core/tracking.rs:1193-1199
  • src/core/tracking.rs:401-418

各カラムの意味

  • timestamp
    • 記録時刻
  • raw_command
    • ユーザーが打ったコマンド文字列
  • error_message
    • parse error 文字列
  • fallback_succeeded
    • fallback 実行に成功したら 1
    • コマンド自体を起動できなければ 0

実 DB のサンプル

このマシンでは 1 件あり、内容は次でした。

  • raw_command: show
  • error_message: error: unrecognized subcommand 'show' ...
  • fallback_succeeded: 0

このテーブルも、入力されたコマンド文字列とエラーテキストがそのまま残る 点が監査上のポイントです。

4. rtk gain はどんなクエリで何を集計しているか

4.1 全体サマリ

rtk gain の既定表示は src/analytics/gain.rsrun() から tracker.get_summary_filtered(...) を呼びます。

根拠:

  • src/analytics/gain.rs:15-63

ただし総計は SQL の SUM(...) 一発ではなく、まず次のクエリで全行を取り、Rust 側で合計しています。

SELECT input_tokens, output_tokens, saved_tokens, exec_time_ms
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)

その後 Rust 側で:

  • total_commands
  • total_input
  • total_output
  • total_saved
  • total_time_ms

を加算し、

  • avg_savings_pct = total_saved / total_input * 100
  • avg_time_ms = total_time_ms / total_commands

を計算します。

根拠:

  • src/core/tracking.rs:508-566

重要なのは、平均節約率は「各行の savings_pct の平均」ではなく、総保存 token / 総入力 token の比率 だということです。

4.2 By Command

既定表示の「By Command」は次の SQL です。

SELECT rtk_cmd, COUNT(*), SUM(saved_tokens), AVG(savings_pct), AVG(exec_time_ms)
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
GROUP BY rtk_cmd
ORDER BY SUM(saved_tokens) DESC
LIMIT 10

根拠:

  • src/core/tracking.rs:569-595

つまりコマンド別には、

  • 件数
  • saved token 合計
  • savings_pct 平均
  • exec_time_ms 平均

を出しています。

4.3 Graph / Last 30 Days

グラフ用の by_day は次の SQL です。

SELECT DATE(timestamp), SUM(saved_tokens)
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
GROUP BY DATE(timestamp)
ORDER BY DATE(timestamp) DESC
LIMIT 30

根拠:

  • src/core/tracking.rs:597-619

これを Rust 側で reverse() して古い日付から描画します。

4.4 --history

rtk gain --history は最近 N 件を次で取得します。

SELECT timestamp, rtk_cmd, saved_tokens, savings_pct
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
ORDER BY timestamp DESC
LIMIT ?3

根拠:

  • src/core/tracking.rs:868-898
  • src/analytics/gain.rs:220-248 相当の表示処理

ここでは original_cmdproject_path は表示に使っていませんが、DB には残っています。

4.5 --daily

SELECT
    DATE(timestamp) as date,
    COUNT(*) as commands,
    SUM(input_tokens) as input,
    SUM(output_tokens) as output,
    SUM(saved_tokens) as saved,
    SUM(exec_time_ms) as total_time
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
GROUP BY DATE(timestamp)
ORDER BY DATE(timestamp) DESC

Rust 側で:

  • savings_pct = saved / input * 100
  • avg_time_ms = total_time / commands

を計算。

根拠:

  • src/core/tracking.rs:643-692

4.6 --weekly

SELECT
    DATE(timestamp, 'weekday 0', '-6 days') as week_start,
    DATE(timestamp, 'weekday 0') as week_end,
    COUNT(*) as commands,
    SUM(input_tokens) as input,
    SUM(output_tokens) as output,
    SUM(saved_tokens) as saved,
    SUM(exec_time_ms) as total_time
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
GROUP BY week_start
ORDER BY week_start DESC

根拠:

  • src/core/tracking.rs:716-767

4.7 --monthly

SELECT
    strftime('%Y-%m', timestamp) as month,
    COUNT(*) as commands,
    SUM(input_tokens) as input,
    SUM(output_tokens) as output,
    SUM(saved_tokens) as saved,
    SUM(exec_time_ms) as total_time
FROM commands
WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)
GROUP BY month
ORDER BY month DESC

根拠:

  • src/core/tracking.rs:791-840

4.8 --failures

rtk gain --failuresparse_failures を見ます。中では少なくとも次を実行しています。

SELECT COUNT(*) FROM parse_failures;
SELECT COUNT(*) FROM parse_failures WHERE fallback_succeeded = 1;

SELECT raw_command, COUNT(*) as cnt
FROM parse_failures
GROUP BY raw_command
ORDER BY cnt DESC
LIMIT 10;

SELECT timestamp, raw_command, error_message, fallback_succeeded
FROM parse_failures
ORDER BY timestamp DESC
LIMIT 10;

根拠:

  • src/core/tracking.rs:421-488
  • src/analytics/gain.rs:359-406

5. --project はどう絞っているか

rtk gain -presolve_project_scope() で現在ディレクトリを正規化し、その絶対パスを project_path フィルタに流します。

根拠:

  • src/analytics/gain.rs:29-30
  • src/analytics/gain.rs:90-98
  • src/analytics/gain.rs:290-296

SQL 側では

WHERE (?1 IS NULL OR project_path = ?1 OR project_path GLOB ?2)

になっており、現在ディレクトリそのものか、その配下サブディレクトリも対象にします。LIKE ではなく GLOB を使っているので、パス中の _% がワイルドカード扱いされません。

根拠:

  • src/core/tracking.rs:40-55
  • src/core/tracking.rs:508-520

6. このマシンの実 DB に対する集計確認

実際に history.db に対して rtk gain 相当の集計を走らせると、以下でした。

  • 総コマンド数: 5
  • 総 input tokens: 339
  • 総 output tokens: 62
  • 総 saved tokens: 277
  • 総実行時間: 19ms

日次・週次・月次はすべて 2026-04-11 / 2026-04-06..2026-04-12 / 2026-04 の 1 バケットにまとまっていました。rtk gain --daily --weekly --monthly の出力もこれと一致しました。

7. セキュリティ監査観点でどう扱うのが望ましいか

7.1 この DB は何が危ないか

この DB は「生の標準出力本文」を持たないので、ログ全文保存 DB ほど重くはありません。ただし、次はそのまま入ります。

  • original_cmd
  • rtk_cmd
  • project_path
  • raw_command
  • error_message

つまり、以下の漏えいは普通に起こり得ます。

  • 開発対象ディレクトリ名、社名、案件名、ユーザー名
  • コマンド引数に含めたパス、ブランチ名、ファイル名
  • 誤って引数に載せた secret 断片
  • parse error に含まれる内部パスやコマンド断片

7.2 望ましい取り扱い

監査観点では、少なくとも次の扱いが妥当です。

  1. history.db を「個人端末ローカル限定の機微データ」とみなす
  2. バックアップ対象にするなら暗号化ストレージ前提にする
  3. サポート依頼や不具合報告時に、この DB をそのまま第三者へ渡さない
  4. MDM / EDR / DLP の監査対象に入れる
  5. 共有端末ではユーザーごとのホーム権限を厳密に分ける
  6. CI や共有 VM では RTK_DB_PATH を隔離パスへ向けるか、利用ポリシー自体を決める

7.3 実装上の注意点

tracking.enabled は少なくともこのコードでは効いていない

Config には tracking.enabled がありますが、今回読んだ範囲では Tracker::record()TimedExecution::* がこれを参照していません。検索上も database_path 以外で tracking.* が使われていませんでした。

根拠:

  • src/core/config.rs:34-49
  • src/core/tracking.rs:1156-1166

したがって、設定ファイルに enabled = false を書いても、少なくとも現行実装では tracking 停止にならない可能性が高い です。

history_days も少なくとも cleanup には使われていない

削除期限は DEFAULT_HISTORY_DAYS 定数で固定されており、config.tracking.history_days を読んでいません。

根拠:

  • src/core/constants.rs:6
  • src/core/tracking.rs:388-397

したがって、設定ファイルで retention を変えられるように見えても、現行実装では 90 日固定運用の可能性が高い です。

これは監査上、「設定と実装がずれている」点として押さえておく価値があります。

8. まとめ

  • 保存先の実体は現行コード上 history.db
  • 保存場所は RTK_DB_PATHconfig.toml → OS 既定 data dir の順
  • この macOS では /Users/${USER}/Library/Application Support/rtk/history.db
  • 主要テーブルは commandsparse_failures
  • commands にはコマンド文字列、cwd 絶対パス、概算 token 数、節約率、実行時間が入る
  • parse_failures には parse 失敗時の生コマンドとエラー文字列が入る
  • rtk gain はこの DB を read-only に集計して表示する
  • 出力本文は常設保存していない
  • ただしコマンド文字列とパスは十分に機微なので、ローカル秘密情報として扱うべき
  • さらに tracking.enabled / history_days はコード上は期待どおり効いていない可能性がある

RTK SQLite 削除機構の読解メモ 004

質問

SQLite ファイルを削除したり、SQLite ファイル内のテーブルを削除する機構は、rtk コマンドのサブコマンドやオプションとして存在していますか。ソースコード読解して調べてほしい。調べた結果を reading/004.md として保存してほしい。

結論

  • rtkSQLite ファイル自体を削除するサブコマンド は存在します。
    • rtk telemetry forget
  • ただしこれは「tracking DB 専用メンテナンスコマンド」というより、telemetry 忘却処理の一部としてローカル DB ファイルを削除する 実装です。
  • 一方で、SQLite ファイル内のテーブルを削除するサブコマンドやオプションは、コード上見当たりません
    • DROP TABLE commands
    • DROP TABLE parse_failures
    • テーブル全消去のための DELETE FROM ... を明示的に実行する CLI
    • VACUUM, TRUNCATE, reset-db のような CLI は見つかりませんでした。
  • なお内部実装としては、追記のたびに 90日より古い行を DELETE する自動クリーンアップ はあります。ただしこれはユーザーが明示的に叩くサブコマンドではありません。

1. SQLite ファイルを削除するサブコマンドはあるか

あります。TelemetrySubcommandForget が定義されています。

根拠:

  • src/core/telemetry_cmd.rs:4-10
  • src/main.rs:546-550

run_forget() の中で、ローカル tracking DB を std::fs::remove_file() で削除しています。

let db_path = dirs::data_local_dir()
    .unwrap_or_else(|| std::path::PathBuf::from("."))
    .join(super::constants::RTK_DATA_DIR)
    .join(super::constants::HISTORY_DB);
if db_path.exists() {
    match std::fs::remove_file(&db_path) {
        Ok(()) => println!("Local tracking database deleted: {}", db_path.display()),
        Err(e) => eprintln!("rtk: could not delete {}: {}", db_path.display(), e),
    }
}

根拠:

  • src/core/telemetry_cmd.rs:109-158

したがって、SQLite ファイル丸ごとの削除機構は rtk telemetry forget として存在する、が答えです。

2. これはどの DB パスを削除するか

ここは注意点があります。

tracking DB を通常開くときは、get_db_path() が次の優先順位でパスを決めます。

  1. RTK_DB_PATH
  2. config.tomltracking.database_path
  3. OS 既定パスの rtk/history.db

根拠:

  • src/core/tracking.rs:1156-1171

しかし rtk telemetry forgetget_db_path() を使っていませんdirs::data_local_dir()/rtk/history.db を直接組み立てて削除しています。

根拠:

  • src/core/telemetry_cmd.rs:131-140
  • src/core/constants.rs:1-2

つまり、少なくとも現行コードでは:

  • 既定パスの history.db は削除対象
  • RTK_DB_PATH で別場所に置いた DB は削除対象外
  • tracking.database_path で別場所に置いた DB も削除対象外

と読むのが自然です。

これは「DB 削除機構はあるが、カスタム DB パスを完全には考慮していない」という実装差分です。

3. SQLite 内テーブルを削除するサブコマンドやオプションはあるか

コード上、見当たりません

見つからなかったもの

  • DROP TABLE commands
  • DROP TABLE parse_failures
  • DROP TABLE
  • テーブル初期化専用のサブコマンド
  • rtk gain --reset
  • rtk telemetry clear-db
  • rtk config reset-tracking

少なくとも src/main.rs のコマンド定義と src/core/telemetry_cmd.rs を見る限り、公開 CLI で tracking DB のテーブルを削除する導線はありません。

根拠:

  • src/main.rs:543-550
  • src/core/telemetry_cmd.rs:4-18

TelemetrySubcommand は以下の 4 つだけです。

  • status
  • enable
  • disable
  • forget

4. 内部実装としての削除は何があるか

4.1 古い行の自動削除

Tracker::record()record_parse_failure() は最後に cleanup_old() を呼びます。

根拠:

  • src/core/tracking.rs:351-385
  • src/core/tracking.rs:401-418

cleanup_old() は 90 日より古い行を削除します。

DELETE FROM commands WHERE timestamp < ?1
DELETE FROM parse_failures WHERE timestamp < ?1

根拠:

  • src/core/tracking.rs:388-398

ただしこれは ユーザー向けサブコマンドではなく、レコード追加時に内部で自動実行される housekeeping です。

4.2 テーブル削除ではない

ここでやっているのは行削除だけです。

  • DELETE FROM commands WHERE timestamp < ?1
  • DELETE FROM parse_failures WHERE timestamp < ?1

であって、

  • DROP TABLE commands
  • DROP TABLE parse_failures

ではありません。

つまり、DB を空にする機構ではないし、テーブルそのものを消す機構でもありません

5. rtk gain や他のサブコマンドに削除オプションはあるか

今回の読解範囲では、ありません。

gain は tracking DB を読むためのコマンドで、削除系オプションは見当たりません。実装も Tracker::new() のうえで集計関数を呼ぶ read path 中心です。

根拠:

  • src/analytics/gain.rs:15-63

また、main.rs 上も tracking DB メンテナンス専用の別サブコマンドは見当たりません。

6. ドキュメントとの関係

ドキュメントには次の記述があります。

  • rtk telemetry forget でローカルデータ削除
    • docs/TELEMETRY.md:127-154
  • DB を消したければ手動で rm ~/.local/share/rtk/history.db
    • docs/guide/analytics/gain.md:128-131

つまり docs 上も基本スタンスは:

  1. CLI で消すなら rtk telemetry forget
  2. それ以外は手動で DB ファイルを削除

であり、テーブル単位削除の公式 CLI は想定されていない ように見えます。

7. 監査向けの整理

監査用に短くまとめると次の通りです。

  • SQLite ファイル削除機構
    • あり
    • rtk telemetry forget
    • ただし telemetry 忘却フローの一部
  • SQLite 内テーブル削除機構
    • 公開 CLI としては見当たらない
  • 内部の行削除機構
    • あり
    • 新規記録時に 90 日超の古い行だけ自動削除
  • 実装上の注意点
    • rtk telemetry forget は既定パスの history.db を直接削除しており、RTK_DB_PATHtracking.database_path のカスタム DB までは消さない可能性が高い

8. 最終結論

質問への直接回答は次です。

  1. SQLite ファイルを削除する機構はあるか

    • ある
    • rtk telemetry forget
  2. SQLite ファイル内のテーブルを削除する機構はあるか

    • 少なくとも公開サブコマンドやオプションとしては存在しない
  3. その他の削除系挙動はあるか

    • ある
    • ただし内部の retention cleanup で、古い行を DELETE するだけ

RTK SQLite 保持期間と 90 日削除トリガーの読解メモ 005

質問

rtk が生成する SQLite ファイルの中身のデータは無期限に保管され肥大化していくのか知りたい。reading/*.md によれば 90 日経過したレコードは削除するとのことだった。それは rtk コマンドで何をしたタイミングで発動するのか知りたい。ソースコード読解して調べてほしい。

結論

  • commands テーブルと parse_failures テーブルのレコードは、設計上は無期限保存ではありません
  • 実装では DEFAULT_HISTORY_DAYS = 90 になっており、90 日より古い行を削除する自動クリーンアップがあります。
    • 根拠: src/core/constants.rs:6, src/core/tracking.rs:388-397
  • ただしこの削除は、rtk のあらゆる起動時に走るわけではありません
  • 実際に発動するのは、新しい tracking レコードを書き込むときです。
    • commands に INSERT した直後
    • parse_failures に INSERT した直後
    • 根拠: src/core/tracking.rs:351-385, src/core/tracking.rs:402-418
  • したがって、たとえば rtk gain のような DB を読むだけのコマンドでは cleanup は発動しません
    • 根拠: src/analytics/gain.rs:29-40
  • さらに、今回読んだ範囲では VACUUMauto_vacuum の設定は見当たりません。そのため、古い行が削除されても SQLite ファイルサイズが即座に小さくなるとは限りません

1. 90 日保持の実装は本当にあるか

あります。保持期間は DEFAULT_HISTORY_DAYS 定数で 90 日です。

pub const DEFAULT_HISTORY_DAYS: i64 = 90;

根拠:

  • src/core/constants.rs:6

削除処理本体は Tracker::cleanup_old() です。

fn cleanup_old(&self) -> Result<()> {
    let cutoff = Utc::now() - chrono::Duration::days(DEFAULT_HISTORY_DAYS);
    self.conn.execute(
        "DELETE FROM commands WHERE timestamp < ?1",
        params![cutoff.to_rfc3339()],
    )?;
    self.conn.execute(
        "DELETE FROM parse_failures WHERE timestamp < ?1",
        params![cutoff.to_rfc3339()],
    )?;
    Ok(())
}

根拠:

  • src/core/tracking.rs:388-398

つまり、少なくともコード上は

  • commands
  • parse_failures

の両方について、90 日より古い行は削除対象です。

2. その削除はいつ発動するか

2.1 commands に書き込むとき

Tracker::record()commands へ INSERT したあと、必ず self.cleanup_old()? を呼びます。

根拠:

  • src/core/tracking.rs:351-385

該当箇所:

self.conn.execute(
    "INSERT INTO commands (...) VALUES (...)",
    ...
)?;

self.cleanup_old()?;

したがって、通常の tracking 記録が 1 件でも発生したタイミングで、90 日超の古い行の削除が走ります。

2.2 parse_failures に書き込むとき

Tracker::record_parse_failure()parse_failures へ INSERT したあと、同じく self.cleanup_old()? を呼びます。

根拠:

  • src/core/tracking.rs:402-418

つまり、parse failure を 1 件記録したタイミングでも cleanup が走ります。

3. 具体的にどんな rtk 実行で cleanup が走るか

3.1 フィルタ対象コマンドを普通に実行したとき

多くのコマンド実装は TimedExecution::track() を使って最後に tracking を記録します。

TimedExecution::track() は内部で Tracker::new() を作り、tracker.record(...) を呼びます。

根拠:

  • src/core/tracking.rs:1292-1305

したがって、たとえば rtk git ..., rtk ls ..., rtk cargo ... などで tracking が記録される経路を通れば cleanup も一緒に走る、という理解でよいです。

3.2 passthrough 記録のとき

TimedExecution::track_passthrough() も内部で tracker.record(...) を呼びます。

根拠:

  • src/core/tracking.rs:1328-1333

そのため、token 数を 0/0 で記録する passthrough 系の tracking でも cleanup は走ります。

3.3 fallback / parse failure のとき

run_fallback() では、成功時も失敗時も record_parse_failure_silent(...) が呼ばれます。これは内部で tracker.record_parse_failure(...) を呼びます。

根拠:

  • src/main.rs:1145-1157
  • src/main.rs:1173-1180
  • src/core/tracking.rs:1193-1198

したがって、Clap 解析に失敗して fallback 経路へ入ったケースでも cleanup が走ります。

4. 逆に、いつ cleanup は走らないか

4.1 Tracker::new() しただけでは走らない

Tracker::new() は DB を開いてテーブルや index を用意するだけで、cleanup_old() は呼びません。

根拠:

  • src/core/tracking.rs:249-326

つまり、DB を開くだけでは古い行は削除されません

4.2 rtk gain のような参照系コマンド

rtk gainTracker::new() で DB を開いたあと、集計メソッドを呼んで表示するだけです。ここには cleanup 呼び出しがありません。

根拠:

  • src/analytics/gain.rs:29-40

したがって、

  • rtk gain
  • rtk gain --history
  • rtk gain --failures

のような 読むだけの操作では、90 日削除の housekeeping は発動しない と読むのが自然です。

5. 「無期限に肥大化するのか」への答え

5.1 レコード件数の意味では「無期限ではない」

コードの意図としては、commandsparse_failures のレコードは 直近 90 日ぶんに保つ設計です。

そのため、「新しい tracking が継続的に発生する」前提なら、行数は基本的に 90 日窓で頭打ちになります。

5.2 ただし、長期間 rtk を使わなければ古い行は残り続ける

cleanup は「経過時間だけ」で自動実行されるのではなく、次回の record / parse failure 記録時にまとめて走る方式です。

したがって、

  • 100 日前の行がある
  • その後しばらく rtk gain しか使わない

という状況では、古い行はその間ずっと DB に残ります

次に tracking を伴う rtk 実行が発生した時点で、そこで初めて削除されます。

5.3 ファイルサイズの意味では「即時には縮まない可能性が高い」

今回読んだ範囲では、

  • VACUUM
  • PRAGMA auto_vacuum
  • PRAGMA incremental_vacuum

は見当たりませんでした。確認できた PRAGMA は journal_mode=WALbusy_timeout だけです。

根拠:

  • src/core/tracking.rs:255-260
  • src/core/tracking.rs:388-397

そのため、古い行を DELETE しても、SQLite ファイル自体のサイズがすぐ小さくなる保証はありません。 この点では「データ行は 90 日で掃除される」が、「ファイルサイズは運用次第でじわじわ大きく見える可能性がある」と整理するのが正確です。

6. 最終回答

質問への直接回答を短くまとめると次の通りです。

  1. rtk の SQLite データは無期限保存ではないか

    • 設計上は無期限保存ではない
    • commands / parse_failures の 90 日超レコードを削除する実装がある
  2. 90 日削除は何のタイミングで発動するか

    • 新しい tracking レコードを書いた直後
    • 新しい parse failure レコードを書いた直後
  3. rtk gain を実行したタイミングで発動するか

    • 少なくとも現行コード上はしない
    • rtk gain は DB を読むだけで cleanup を呼ばない
  4. SQLite ファイルは肥大化しないか

    • レコード件数は 90 日窓で抑える意図
    • ただし cleanup は write 時のみ
    • さらに VACUUM が見当たらないので、ファイルサイズは DELETE 後も即縮小しない可能性が高い

RTK SQLite を生成させない・記録させない利用方法の読解メモ 006

質問

rtk コマンドを利用するが、SQLite ファイルを生成させない、記録させない、という利用方法は存在するか調べてほしい。rtk コマンドで特定のオプションで 1 を付けて 1 回だけ実行するとか、設定ファイルを置くとか、環境変数の何かに 1 を設定するとか。ソースコード読解して調べてほしい。

結論

  • 公式に用意された「tracking を無効化する CLI オプション / 環境変数 / 設定」は、現行コード上は見当たりませんでした。
  • config.toml には [tracking].enabled という項目が存在し、ドキュメントも「enable/disable token tracking」と説明しています。
    • しかし、実装側でこの値を参照して tracking を止めるコードは見つかりませんでした
    • 根拠: src/core/config.rs:35-48 と、tracking.enabled の参照検索結果
  • 同様に tracking.history_days も設定項目として存在しますが、cleanup 側は 固定の DEFAULT_HISTORY_DAYS = 90 を使っており、実装上は設定値を使っていません。
    • 根拠: src/core/config.rs:35-48, src/core/tracking.rs:388-389
  • したがって、正攻法として「SQLite を生成しない・記録しない」利用方法は、ソースコード上は実装されていないと結論づけるのが妥当です。

ただし、実装の癖を使った非公式な回避策はあります。

  • tracking は Tracker::new() に失敗すると静かに諦める実装です。
    • 根拠: src/core/tracking.rs:1195-1198, src/core/tracking.rs:1297-1305, src/core/tracking.rs:1331-1333
  • そのため、RTK_DB_PATH を「SQLite を開けないパス」に向けると、コマンド実行自体は続くが tracking だけ失敗して記録されない動きになります。
  • ただしこれは正式サポートされた使い方ではなく、実装依存のワークアラウンドです。

1. まず、tracking を止める専用オプションはあるか

少なくとも main.rs 上、--no-track, --disable-tracking, --no-history のような tracking 無効化専用 CLI フラグは見当たりませんでした。

根拠:

  • src/main.rs のオプション検索

つまり、

  • rtk --no-track ...
  • rtk --disable-tracking ...
  • rtk gain --no-history

のような「1 回だけ記録しない」ための公開オプションは、現行コードには見当たりません。

2. 設定ファイルで止められるか

2.1 設定項目そのものは存在する

ConfigTrackingConfig には次のフィールドがあります。

pub struct TrackingConfig {
    pub enabled: bool,
    pub history_days: u32,
    pub database_path: Option<PathBuf>,
}

デフォルトも enabled: true です。

根拠:

  • src/core/config.rs:35-48

ドキュメントにも次のように書かれています。

[tracking]
enabled = true              # enable/disable token tracking
history_days = 90           # retention in days (auto-cleanup)
database_path = "/custom/path/tracking.db"

根拠:

  • docs/guide/getting-started/configuration.md:24-29
  • src/core/README.md:64-67

2.2 しかし enabled は実装上使われていない

コード全体を見ても、config.tracking.enabled を参照して

  • Tracker::new() を呼ばない
  • record() をスキップする
  • SQLite を作らない

といった分岐は見当たりませんでした。

一方で get_db_path()database_path だけを見ています。

fn get_db_path() -> Result<PathBuf> {
    if let Ok(custom_path) = std::env::var("RTK_DB_PATH") {
        return Ok(PathBuf::from(custom_path));
    }

    if let Ok(config) = crate::core::config::Config::load() {
        if let Some(db_path) = config.tracking.database_path {
            return Ok(db_path);
        }
    }

    ...
}

根拠:

  • src/core/tracking.rs:1156-1171

つまり、[tracking] enabled = false を設定しても、現行コード上は tracking 停止にはつながりません

3. 環境変数で止められるか

3.1 tracking 専用の無効化環境変数は見当たらない

tracking に関係する環境変数として実装上確認できたのは、少なくとも以下です。

  • RTK_DB_PATH
    • DB ファイルの保存先を変えるだけ
  • RTK_TELEMETRY_DISABLED
    • telemetry の送信停止用

このうち RTK_TELEMETRY_DISABLED=1ネットワーク telemetry を止めるだけで、SQLite tracking の停止ではありません。

根拠:

  • src/core/tracking.rs:1157-1165
  • src/core/telemetry.rs
  • README.md:443

つまり、

  • RTK_TELEMETRY_DISABLED=1
  • rtk telemetry disable

では ローカル SQLite 記録は止まりません

3.2 RTK_DB_PATH は停止ではなく保存先変更

RTK_DB_PATH は最優先で DB パスを差し替えます。

根拠:

  • src/core/tracking.rs:1157-1159

これは本来、

  • デフォルト位置に置きたくない
  • 任意の場所に history.db を置きたい

ためのもので、tracking を無効化する目的の公式機能ではありません

4. tracking はどこで必ず走る設計か

tracking 実装は TimedExecution::track()TimedExecution::track_passthrough() に集約されています。

track() は毎回 Tracker::new() を作って record() を呼びます。

if let Ok(tracker) = Tracker::new() {
    let _ = tracker.record(...);
}

track_passthrough() も同様です。

根拠:

  • src/core/tracking.rs:1292-1305
  • src/core/tracking.rs:1328-1333

さらに fallback 系の parse failure 記録も

if let Ok(tracker) = Tracker::new() {
    let _ = tracker.record_parse_failure(...);
}

です。

根拠:

  • src/core/tracking.rs:1195-1198

この設計から見ると、tracking を止める公式スイッチがない限り、通常の rtk 実行は記録しようとする構造です。

5. 非公式な回避策はあるか

5.1 RTK_DB_PATH を壊れたパスに向ける

Tracker::new()

  1. DB パスを決める
  2. 親ディレクトリを create_dir_all
  3. Connection::open(&db_path)?

という流れです。

根拠:

  • src/core/tracking.rs:249-255

そして、track() / track_passthrough() / record_parse_failure_silent() は、Tracker::new() が失敗した場合にエラーを上に返さず黙って tracking を諦めます

根拠:

  • src/core/tracking.rs:1195-1198
  • src/core/tracking.rs:1297-1305
  • src/core/tracking.rs:1331-1333

なので例えば、

env RTK_DB_PATH=/dev/null/history.db rtk ls reading

のように、SQLite を開けないパスへ向けると、tracking 初期化に失敗して記録されない挙動になります。

この環境でも上のコマンドを軽く試したところ、rtk ls reading 自体は動作しつつ、tracking 側は失敗させられる構図でした。

ただしこれは次の意味で非推奨のワークアラウンドです。

  • 実装依存であり将来壊れうる
  • rtk gain など tracking DB を読むコマンドは当然壊れる
  • エラーパスを利用しているだけで、正式な disable 機能ではない

5.2 読み取り専用・書けない場所へ向ける

同じ理屈で、RTK_DB_PATH または tracking.database_path

  • 書けないディレクトリ
  • 作成不能なパス

に向けても、tracking は失敗して黙ってスキップされる可能性があります。

ただしこれも正式サポートではなく、環境依存なので、「そういう実装上の逃げ道はある」以上の意味は持たせない方がよいです。

6. ドキュメントと実装のズレ

今回の読解では、少なくとも次のズレがありました。

  1. tracking.enabled

    • ドキュメントでは enable/disable と説明
    • しかし実装では参照されていない
  2. tracking.history_days

    • ドキュメントでは設定可能と説明
    • しかし cleanup は DEFAULT_HISTORY_DAYS = 90 固定を使用

根拠:

  • docs/guide/getting-started/configuration.md:24-29
  • src/core/README.md:64-67
  • src/core/config.rs:35-48
  • src/core/tracking.rs:388-389

つまり、ドキュメントや config struct が示しているより、現行実装は tracking の ON/OFF や retention 設定に未対応と読むのが自然です。

7. 最終回答

質問への直接回答を短くまとめると次の通りです。

  1. rtk を普通に使いつつ、SQLite を生成させない・記録させない公式な方法はあるか

    • 現行コード上は見当たらない
  2. rtk の CLI オプションで 1 回だけ無効化できるか

    • 見当たらない
  3. 設定ファイルで無効化できるか

    • tracking.enabled = false という項目はある
    • ただし 実装上は使われていないので無効化できない
  4. 環境変数で無効化できるか

    • telemetry を止めるものはある
    • しかし tracking/SQLite を止める専用環境変数は見当たらない
  5. 実装依存の回避策はあるか

    • ある
    • RTK_DB_PATH を SQLite を開けないパスへ向けると、tracking 初期化失敗により記録されない
    • ただし これは非公式ワークアラウンド
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment