0009: 常設ダッシュボード基盤と行動ログ統計 — 「外部に脳みそを増設する」
背景
0007のQuartoダッシュボード(追記)は 「1分析=1ファイルの静的レポート」であり、常時更新される監視型ダッシュボード ではない。ユーザーの要求はその先にある: e-Statに限らず、自分自身の行動履歴まで 統計処理してダッシュボード化し、健康管理を含む生活全般の監視に使う。 特性上苦手な領域(自己モニタリング等)を、外部システムに委譲して補う—— 「外部に脳みそを増設する」というLLM-Wiki以来の一貫した思想の適用である。
ユーザーは当初 Apache Superset 系統を希望した。しかし実測(2026-07-09)で 本vpsはRAM 1.6GB・空き850MB(deeptutor/forgejo/caddy稼働中)であり、 Superset(公式推奨8GB、web+worker+Redis+PostgreSQLで最低2GB超)は物理的に 載らない。mac-miniは「アップデート等で落ちるリスクがあるため依存させない」 方針(0008追記)で、移設先にできない。
決定
1. 三層構成
受け皿(SQLite/DuckDB。行動ログ・集計テーブルの置き場)
← R / AIエージェントが統計処理して集計テーブルを書き込む
ダッシュボード(Grafana。常設・自動更新・アラート)
静的レポート(Quarto dashboard。0007追記のまま、深掘り分析用)
- 受け皿:
knowledge/配下にSQLite(必要になったらDuckDB併用)。 architecture.mdの状態DBスタックの範囲内で、新規ミドルウェアなし。 - 統計処理: Rスクリプト(cron/Goose schedule実行)またはAIエージェントが 生ログ→集計テーブル(日次サマリ等)を書く。ダッシュボードは集計済み テーブルだけを読む(生ログを直接可視化しない。処理の重心をDB側に置かない)。
- 可視化・監視: Grafana(Docker、実測100〜150MB級)。 architecture.mdで元々「監視」候補だったものを正式採用。アラート (閾値超え通知)を持つため「監視するのに使いたい」要求を満たす。
2. Supersetの扱い
不採用ではなく保留。SQLベースのBI探索としてSupersetが優る場面はあるが、 現vpsのリソースでは起動すら不可。RAM増強またはrtx-worker常時稼働化の際に 再評価する。受け皿をSQL(SQLite/DuckDB)にしておくことで、将来Supersetを 足す場合もデータ層は無変更で済む。
3. 行動ログのソース(段階導入)
- 既に機械可読なものから始める: Goose/DeepTutorの実行ログ、 llmwiki ingestの件数、
ob syncの履歴、git活動等(すべてvps内で完結)。 - 手入力の健康・生活記録はObsidianのデイリーノート形式で書き、 パーサで受け皿へ取り込む(Obsidian Syncは既存運用のまま)。
- 端末の行動計測(ActivityWatch等)は候補に留める(未決定)。
4. プライバシー
行動・健康データはknowledge/配下(Git管理外)とR2のみに置き、 リポジトリ・外部SaaSに出さない。GrafanaはCloudflare Zero Trust背後 (既存のCaddy構成に載せる)でのみ公開する。
理由
- RAM 850MBという制約下で「常設・自動更新・アラート」を満たす現実解は Grafana一択に近い(Metabase/Redash/SupersetはいずれもGB級)。
- 「R+AIエージェントで統計を出し、ダッシュボードは表示に徹する」構成は、 0007(R採用)・0002(Goose)・0008(エージェント運用)の既存資産を そのまま活かし、新規学習コストがGrafanaのみ。
- 受け皿をSQLにしておけばSuperset移行時もデータ層無変更(決定2)。
影響
architecture.md: 監視行のGrafanaを「採用(0009)」に更新。 技術スタック表に「行動ログ受け皿 = SQLite/DuckDB」を明記。docs/tasks/backlog.mdに追加: Grafana導入(Caddy/Zero Trust配下)、 行動ログ受け皿スキーマ+最初の取り込み(機械可読ログ1種)、 集計Rスクリプト雛形。
追記: 第一段はQuarto静的ダッシュボード(ゼロ常駐)に変更(2026-07-09)
「Grafanaより軽くできないか」というユーザーの問いを受けて再検討し、 第一段の可視化をQuarto format: dashboard の静的HTMLに変更する (実測: render 5.1秒、自己完結HTML 2.7MB、この環境で動作確認済み)。
受け皿(SQLite) ← R集計スクリプト(cron/Goose schedule)
├─ 閾値判定→通知(アラートはここで完結。ntfy等)
└─ quarto render → 静的HTML(既存Caddyで配信)
- 常駐RAM 0(renderはcron時に数秒だけ)。新サービス・新攻撃面なし。
- 行動ログの粒度は日次〜時間単位なので、15分〜1時間の再renderで監視要件を満たす。 自動リロードはmeta refresh、対話性はojs(0007追記と同じ)。
- アラートをGrafanaのエンジンではなく集計スクリプト側の閾値判定に置くことで 中間層を丸ごと省く。
- Grafanaはアップグレード条件付きの保留に格下げ: 秒〜分単位のリアルタイム 監視、または時間範囲のズーム/パン探索が必要になった時点で導入 (その場合も受け皿SQLiteは無変更)。決定1の表記はこの追記が上書きする。
追記: 公開層をCloudflare Pagesへ分離(2026-07-09、ユーザー提案)
静的HTML化したことで、配信をvpsからCloudflare Pagesに分離できる。 利点は2つ: ①表示させやすい(CDN・カスタムドメイン)、②vpsが停止しても 最後にrenderされたダッシュボードは見え続ける——監視対象と監視画面が 同時に死ぬ自己矛盾の解消(renderのgenerated_at表示で鮮度も分かる)。
- デプロイ: render後に
npx wrangler pages deploy <出力dir> --project-name=<名>(ホストにnode v22あり、確認済み)。APIトークンはPagesスコープで発行し、.envのCF_PAGES_API_TOKENから環境変数で渡す(コミット・ログ禁止)。 - 必須条件: 行動・健康データの集計を含むページは、Pagesプロジェクトに Cloudflare Access(既存Zero Trust)のポリシーを必ず掛ける。 決定4のプライバシー原則はこの条件下で維持される。
- R2公開バケット+カスタムドメイン案も検討したが、index解決やAccess連携の 素直さでPagesが優る。D1保留の「Web配信層」はこのPages/Workers層を指す (静的ダッシュボードの段階ではD1不要のまま)。
- vps側のCaddy配信は開発時プレビュー用として残す。
追記: Grafana線の確定とCloudflare D1の位置づけ(2026-07-09)
ユーザー確認によりGrafana線で確定。Cloudflare D1(マネージドSQLite)は 検討の結果、受け皿には使わない:
- D1はWorkersから使う前提の設計で、外部からはHTTP REST経由になる。 RはローカルSQLiteなら
RSQLiteで直結できるところが全てHTTP呼び出しになり、 GrafanaにはD1データソースが存在しないためシムが1枚要る。三層の両端で 複雑さが増える割に、ローカルSQLiteの弱点(耐久性)はR2一方向syncへの .sqlite/ダンプ載せで既存経路のまま埋まる。 - D1はWeb配信層用に保留:
web/appで「公開してよい統計だけ」を配信する 段階になれば、Workers+D1が適材。決定4のプライバシー原則(行動・健康 データはknowledge/とR2のみ)とはこの住み分けで整合する。
未決定事項
- 行動ログの具体的なスキーマ(イベント型 vs 日次サマリ型)。
- ActivityWatch等の端末計測を導入するか。
- 健康記録のデイリーノート書式。
- Superset再評価のトリガ(RAM増強の時期)。