Insights·2026-08-07

KakaoTalk のローカル DB をどう読むのか、そしてなぜ Mac でしかできないのか

KakaoTalk には、他のプログラムが会話を読み取るための公式 API がない。会話をコードで扱いたければ、Mac 版 KakaoTalk が自身のアプリコンテナに残しているデータベースファイルを直接開くしかない。このファイルは SQLCipher で丸ごと暗号化されており、鍵はこの Mac のハードウェア UUID と Kakao アカウントの内部番号から導出される。ハードウェア識別子の読み方も、アプリがファイルを置く場所も、そのフォルダを守る権限の仕組みもすべて macOS のものなので、このパイプラインは Mac でしか動かない。さらに cron では動かず、エラーではなく静かに停止する。最初の段さえ越えれば残りは平凡な作業で、ルームごとに新着だけを取り出し、ルーム単位で要約し、Slack に送り、Slack のボットがチケット本文を書いて Jira に登録する。

카카오톡 DB 복호화는 맥에서만 성립한다 — 복호화 CLI와 LaunchAgent 등록 명령, cron과 LaunchAgent 대비를 담은 요약 도식
cron은 권한 대화상자를 못 띄워 조용히 멈춘다. LaunchAgent는 로그인 세션 안에서 돌아 프롬프트를 띄울 수 있다.

パイプラインは四段だ

まず全体像から。毎朝 KakaoTalk のオープンチャットの会話を要約して Slack チャンネルに投稿し、その Slack スレッドから AI がチケット本文を書いて Jira に登録する。四段に分けると、KakaoTalk から会話を読む、ルームごとに要約する、Slack に送る、Slack スレッドを Jira の課題にする、である。

二段目から四段目は難しくない。要約はテキストを大規模言語モデルに渡すだけ、Slack は webhook の URL ひとつで投稿でき、Jira には正式な REST API がある。三つとも文書化された正規の経路だ。

問題は一段目にある。KakaoTalk には、他のプログラムが「このルームの会話をくれ」と尋ねられる公式の窓口がない。Kakao が公開している開発者向け API はログインやメッセージ送信、共有といった外向きのもので、自分の履歴を読み取る内向きのものではない。だからこの一段だけで、残り三段を合わせたより多くの手間がかかる。

会話をコードで読む唯一の入口

本当に他に手がないのかを確かめておく。手作業の道はある。チャットルームで会話をエクスポートすれば txt や csv のファイルが得られるし、実際その方式で取り出したファイルも使っている。しかし毎朝無人で回るパイプラインには使えない。ルームごとに人が押さなければならないからだ。

残る選択肢はひとつ。KakaoTalk がすでに自分のマシンに保存しているデータを直接開くことだ。Mac 版 KakaoTalk は受信したメッセージをアプリコンテナ内のデータベースに蓄えている。コンテナとは macOS がアプリごとに用意する専用フォルダで、アプリは原則として自分のコンテナの中だけで動く。

ひとつはっきりさせておきたい。これは他人のデータを覗く話ではない。自分の Mac に自分でログインして受け取った自分の会話を、その Mac の持ち主が読むという話だ。とはいえ方式自体は Kakao が開けた扉ではなく裏口なので、Kakao が保存方式を変えればいつでも途切れる前提で使うべきものだ。

暗号化された DB の鍵はどう作られるのか

暗号化された KakaoTalk DB の二重の壁と鍵の材料をまとめた図

コンテナのフォルダを開いても、会話がすぐ見えるわけではない。壁が二枚ある。

一枚目はファイル名だ。データベースは kakaotalk.db のような読める名前ではなく、78 桁の十六進文字列で、しかも端末ごとに異なる。アカウント番号と端末識別子から計算された値だからだ。つまりフォルダを覗いても、どれが会話の DB かは名前からは分からない。

二枚目は暗号化だ。ファイルそのものが SQLCipher で丸ごと暗号化されている。SQLCipher は SQLite のデータベースをファイル単位で暗号化する拡張で、通常の sqlite3 コマンドで開くと「これはデータベースファイルではない」というエラーになる。パスフレーズを知ったうえで sqlcipher コマンドで開く必要がある。

ではそのパスフレーズはどこから来るのか。ユーザーが決めたパスワードではない。プログラムが二つの材料から計算する。ひとつはこの Mac のハードウェア UUID、もうひとつは Kakao アカウントの内部番号だ。これらを混ぜて PBKDF2 という標準の鍵導出関数に十万回かける。PBKDF2 は短い材料を反復計算で引き伸ばし、総当たりを高価にするための標準手順である。

面白い点がひとつある。アカウント番号は設定ファイルにそのまま書かれていない。ハッシュ、つまり元の値が分からないよう一方向に潰した結果だけが残っている。だから 0 から順に数値を入れて同じハッシュになる値を探す。アカウント番号はそれほど大きくない整数なので、この方法で現実的な時間に収まる。

これらは私が突き止めたものではない。blluv という開発者が公開した gist と kakaocli プロジェクトが先に整理しており、私のコードもヘッダのコメントにその二つの出典を明記している。だからこの記事でも構造と制約までを説明し、そのままコピーして使える鍵導出コードは載せない。必要なら原典を見ればよい。

なぜ Mac でしかできないのか

このパイプラインがMacでのみ成立する四つのmacOS依存要素をまとめた図

Windows や Linux に移せないかとよく聞かれる。今のコードはそこでは動かない。実装が macOS に縛られている箇所が四つある。

第一に、鍵の材料であるハードウェア識別子が macOS のものだ。macOS の ioreg コマンドで読む IOPlatformUUID であり、Windows に同じ値はない。

第二に、ファイルの置き場所が macOS のサンドボックス構造だ。~/Library/Containers の下にアプリのバンドル ID でできたフォルダがあり、その中に DB がある。Windows 版 KakaoTalk は保存場所もファイル構造も違う。

第三に、アクセス権限の仕組みが macOS の TCC だ。TCC はあるプログラムが他のアプリのデータに触れようとするとき、ユーザーに許可を求める macOS のプライバシー保護機構で、他の OS に対応物はない。

第四に、モバイルはそもそも対象外だ。iOS と Android はアプリ間のデータアクセスを OS レベルで遮断している。つまりこの実装が成立する条件は Mac 版 KakaoTalk である。

誤解をひとつ正しておく。Windows では会話を読めない、という意味ではない。Windows 版 KakaoTalk はメッセージを %LocalAppData% 配下に .edb ファイルとして置き AES で暗号化しており、その鍵導出の手順は韓国のフォレンジック研究で既に公開されている(blog.system32.kr など)。つまり Windows への移植は塞がれた問題ではなく、鍵導出をあちらの規則で書き直す作業だ。この記事が Mac だけを扱うのは私のパイプラインが Mac で動くからであって、Windows が閉じているからではない。

実務的に残る制約はひとつ、パイプラインを回す Mac を一台つけっぱなしにすることだ。

cron では動かない — LaunchAgent でなければならない理由

毎朝回る仕事なので cron に置けばよさそうに見える。実際そうしてみて、失敗した。失敗の仕方が厄介なので書き留めておく。

エラーが出ない。拒否のメッセージも出ない。その場で止まるだけだ。原因は TCC である。KakaoTalk のコンテナを読むにはフルディスクアクセスが必要で、macOS はこの権限を求めるときに画面上へダイアログを出す。ところが cron はバックグラウンドのデーモンなので、ユーザーの画面に窓を出す手段がない。だから尋ねることもできず、拒否されたわけでもなく、返ってこない答えを待ってぶら下がる。

解決は実行主体を変えることだ。cron ではなく LaunchAgent を使う。LaunchAgent はログイン中のユーザーセッション内で動く macOS 標準のスケジューラなので、権限ダイアログを出せるし、一度許可されればその後は静かに回る。

ここから派生する罠がもうひとつある。権限は実行ファイル単位で付く。sqlcipher コマンドに KakaoTalk のコンテナを直接読ませると sqlcipher のバイナリにも別途権限が必要になり、Homebrew が更新するたびにプロンプトが復活する。そこで Python がコンテナから /tmp にファイルを複製し、sqlcipher はその複製だけを触るようにした。権限を持つ主体をひとつに絞ったわけだ。

復号された平文の DB にも気を配る必要がある。/tmp にそのまま置くと既定の権限では同じ端末の他プロセスから読めてしまう。会話の全文が入ったファイルなので、書き出した直後に所有者のみへ絞る。

復号に必要な CLI と LaunchAgent の登録
# SQLCipher CLI — brew autoremove로 지워지면 파이프라인이 통째로 멈춘다
brew install sqlcipher

# cron이 아니라 LaunchAgent로 등록한다
launchctl bootstrap gui/$UID ~/Library/LaunchAgents/com.edb.kakao-briefing.plist
launchctl list | grep kakao-briefing

読んだあと — 新着だけを取り、ルームごとに要約する

ここまで来ると手元に平文の SQLite ファイルがひとつ残る。以降は平凡なデータ処理だ。

毎回すべてを要約するのはコストも時間も見合わないので、ルームごとに最後に処理したメッセージ番号を記録し、それ以降のものだけを取り出す。いわゆるハイウォーターマーク方式である。週末に実行が飛んでもメッセージが失われないよう遡り幅は広めに取り、重複はメッセージ番号が止める。

メッセージはどれも同じ種類ではない。DB にはテキストのほか画像、広告、システム通知が同じテーブルに混在し、種類ごとに番号が付いている。テキストと返信・リンク共有だけを取り込み、残りは捨てる。このフィルタを外すと要約プロンプトが広告文で埋まる。

要約はルームをひとつずつ呼び出す。最初は全ルームをまとめて一度に投げていたが、メッセージが急増したルームがタイムアウトすると、その呼び出しに束ねられた他のルームの要約まで丸ごと消えた。ルームごとのループに個別の例外処理を置けば、一室が倒れても残りは生き残る。ルームごとに直近メッセージ数の上限も設けている。

コストも分けた。一般のオープンチャットの要約はローカルで動く小さなモデルが処理し、正確さが要る業務関連のルームと日程抽出だけを上位モデルに任せる。失敗したときは自動的に上位へ切り替わる。

Slack から Jira へ — AI が書くのは PRD ではなくチケット本文だ

要約ができたら Slack の webhook でチャンネルに投稿する。webhook は Slack が発行する URL ひとつに文字列を POST するとチャンネルに投稿される、最も単純な連携だ。

ここに実装上の罠がひとつ。最初の版は要約 markdown の見出し行を正規表現で探してルームごとの本文を切り出していた。ところが Telegram 向け出力をプレーンテキストに変えた際に見出し記号が消え、この解析が静かに壊れた。今は要約の段でルーム名をキーにした JSON を別に落とし、Slack 送信はその JSON を読む。表示形式が変わってもデータ経路は揺れない。

Slack に載ってからは二つ目のボットの領分だ。このボットは Socket Mode で接続する。サーバーに公開 URL を開かず Slack 側へ接続を張る方式なので、社内ネットワークや個人サーバーでもファイアウォールに穴を開ける必要がない。

ボットをメンションすると、そのメッセージが大規模言語モデルに渡り構造化 JSON が返る。項目は課題を作るかどうか、どのプロジェクトか、種別と優先度、そしてタイトルと本文だ。本文は症状・再現手順・期待結果の三つを書かせる。

ここで言葉を正確にしておきたい。この段をよく「AI が PRD を書く」と表現するが、実際の成果物は別立ての企画書ではなくチケット本文である。プロダクト要求仕様書を一本作っているのではなく、開発者がそのまま着手できるチケットを一枚埋めているのだ。この区別を曖昧にすると期待がずれる。

判断は確信度で切る。0.8 を超えればそのまま Jira に作成し、スレッドにリンクを返信する。0.5 から 0.8 の間なら作るかどうかを人に尋ねる。0.5 未満はログだけ残して無視する。同じメッセージが二度処理されないよう、メッセージのタイムスタンプで重複を防ぐ。

人が残る場所はまさにここだ。繰り返しの集約・整理・作成は機械がやり、人は曖昧なものを裁定し、作るか作らないかを決める。

Kanana が B2B に来れば、この層は消える

このパイプラインの一段目は、公式の窓口がないから作った迂回路だ。迂回路の寿命は正門が開く日までである。

Kakao はすでにその方向を示している。2026 年 4 月 23 日、ソウル COEX の World IT Show で、Kakao は AI ブランド Kanana の『今日のブリーフィング』を公開した。ユーザーの過去の会話を分析し、重要な予定や記念日、やることを要約する機能だ。待ち合わせ場所を決める会話で店を勧めたり、誕生日の話題を検知してギフト機能と連携したりする文脈認識も併せて紹介された。私が DB を復号して作ったものと目的は同じである。

ただし発表された範囲は個人向けだ。B2B や企業向けへの言及はなかった。そして今必要なのは個人の一日のブリーフィングではなく、組織チャンネルの会話が業務チケットにつながる層である。

その層が開く余地はあると見ている。2026 年 7 月、Kakao は Kanana-2 の軽量モデル四種をオープンソースで公開し、ツール呼び出し能力を強調した。ツール呼び出しとは、モデルが自ら外部システムの機能を呼び出すことだ。会話を読んで判断し、別のシステムに登録する仕事はまさにその能力の用途である。

その日が来れば私の復号コードは廃棄される。それが正常だ。迂回路は正門がないときだけ価値があり、正門ができたら未練なく捨てるのが正しい。自動化資産を作るときは、この点を最初から計算に入れておくほうがよい。どの層が一時的な迂回路で、どの層が長く使える資産なのかを区別しておけば、プラットフォームが動いたときに作り直す範囲が明確になる。

このパイプラインで長く残るのは復号ではなく後段だ。ルームごとに要約を分ける設計、確信度で自動と確認を分ける関門、重複を防ぐキー設計は、入力が KakaoTalk であろうと公式 API であろうとそのまま使える。