Repository navigation
feat(permissions): 外部トークンにトークン単位の scope と名前による帰属を持たせる (GitHub PAT 式、#712 の将来項目) #1292
Copy link
Copy link
Open
Labels
✨FeatureThis adds/improves/enhances a featureThis adds/improves/enhances a feature🔒SecuritySecurity related issue/PRSecurity related issue/PR
Description
何を
外部アプリ向けの永続トークンに、トークン単位の権限 scope と名前による帰属を持たせる (GitHub の PAT 式)。principal は external のまま増やさず、各トークンの scope は external に許された範囲の部分集合とする。確認ダイアログと Spotlight の帰属表示はトークン名で出す。
なぜ
- 今は external が 1 principal なので、Raycast (feat: ランチャ拡張 — Raycast / Alfred / Ulauncher 等から検索・投稿・コマンドパレット呼び出し #511) と Stream Deck (feat: Stream Deck プラグイン — 19820 HTTP API 経由でデッキ操作 #510) のように複数の連携を同時に使うと、全部が同じ権限を共有する。片方のために書込を開けるともう片方にも開く
- 確認ダイアログは「外部アプリ」の 1 ラベルしか出せず、どのトークンの要求かを利用者が区別できない。良性の連携の直後に別の連携が投稿を要求しても見分けがつかない
- 権限設定を principal 分離モデルに再設計する(「誰に許すか」で認可を分ける) #712 が「将来: トークン単位 scope (GitHub PAT 式)」を予約済み。principal を動的に増やす案は 権限設定を principal 分離モデルに再設計する(「誰に許すか」で認可を分ける) #712 の方針 (hard ceiling を新設しない / principal 別の保存設定に化けさせない) と逆方向で、権限ウィンドウの行・golden vector・Rust の principal 型をすべて触ることになるので採らない
- Extension (meta: Extension — 任意の言語 (Rust / TypeScript 等) で書き、外部プロセス + HTTP API で本体に繋ぐ拡張 #1291) はこれを前提条件にする。Extension ごとに別のトークンを発行すれば、同じ仕組みで権限と帰属が分かれる
信頼モデル (正本、#1291 の批評で確定)
- external principal は、同じ OS ユーザーで動くネイティブプロセスに対する安全境界ではない。 本体は起動ごとの全権トークンをデータディレクトリに置き、それを読めるローカルプロセスを本人と同格に扱う (生の Misskey ルートは gate を免除、capability 経路は external として判定するが、OS キーチェーンと DB と生ルートで全権に届く)。OS キーチェーンと notemaid の socket も同一ユーザーなら触れる。Raycast / Stream Deck / MCP クライアント / CLI / Extension はすべてこの位置に居る
- したがってトークン単位の scope と帰属の価値は、行儀のよい連携の事故防止と、誰が何をしたかの可視化にある。悪意あるプロセスを同一ユーザーで動かした時点でユーザー権限は渡る。これを隠すと「外部アプリは読取だけ」が表示上の嘘になる。SECURITY.md の該当記述はこの issue の実装と同じ PR で直す
- 信頼の単位は「利用者が自分でトークンを発行した / 実行ファイルを置いてオンにした」こと (VS Code / ブラウザ拡張と同じ)
設計
- トークンの発行時に名前と scope (external の権限の部分集合) を選ぶ。既定は external の既定と同じ (読取だけ)
- 実効権限 = external の grant ∩ トークンの scope。external 側を狭めれば全トークンが狭まる (上限は external のまま)
- HTTP 経由の capability 実行に「どのトークンか」を運び、確認ダイアログ / Spotlight / 受信トレイにトークン名を出す
- 失効は即時。張りっぱなしの SSE 接続にも効かせる (今は接続時だけ検証している)
- 第三の種別「Extension」 (meta: Extension — 任意の言語 (Rust / TypeScript 等) で書き、外部プロセス + HTTP API で本体に繋ぐ拡張 #1291): 名前と scope は永続 (マニフェスト由来)、秘密は起動ごとに回してディスクに書かず、本体の起動時に前回分を全部失効する
- どのトークンかを運ぶ: HTTP の gate に付ける目印と、WebView の dispatcher に渡す実行要求の両方にトークン id を載せる。認可は Rust の per-route gate と WebView の dispatcher の二重実装 (golden vector で一致検査) なので、scope の判定も両方に入れる
- external の書込に著者を刻む: メモの author は今も principal から刻まれている (external は一律「External app」、plugin は id つき)。これをトークン単位の粒度に上げる。欄は persona の id と同じ欄なので、名前空間を分けて (
token:接頭辞など) 衝突を塞ぐ。権限設定を principal 分離モデルに再設計する(「誰に許すか」で認可を分ける) #712 決定 3 (第三者が書いたメモを AI に読ませない) は、system prompt への注入が廃止され AI は検索・一覧の tool でメモを読むので、制御点は tool の既定 (第三者の著者を既定で除外し、同意で含める) になる - 権限ウィンドウの external 行の下に発行済みトークンの一覧 (名前 / scope / 最終使用 / 失効ボタン)
採らない形
- Extension や連携ごとの principal を増やす: 上記のとおり 権限設定を principal 分離モデルに再設計する(「誰に許すか」で認可を分ける) #712 の方針と逆で、触る範囲が広すぎる
- トークンに external を超える権限を持たせる: 上限は external の恒久 deny を含めて変えない
関連
- 権限設定を principal 分離モデルに再設計する(「誰に許すか」で認可を分ける) #712 principal 別の認可 (「トークン単位 scope」の将来項目)
- meta: Extension — 任意の言語 (Rust / TypeScript 等) で書き、外部プロセス + HTTP API で本体に繋ぐ拡張 #1291 Extension (前提条件としてこの issue を指す)
- feat: Stream Deck プラグイン — 19820 HTTP API 経由でデッキ操作 #510 Stream Deck / feat: ランチャ拡張 — Raycast / Alfred / Ulauncher 等から検索・投稿・コマンドパレット呼び出し #511 Raycast (今日この問題を持っている連携)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
✨FeatureThis adds/improves/enhances a featureThis adds/improves/enhances a feature🔒SecuritySecurity related issue/PRSecurity related issue/PR
Activity