@bluelamp/cli-token 1.1.0 → 1.3.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,6 +28,8 @@ const maybe = tryReadCliToken();
28
28
  | core(I/O なし) | `encryptPayload` / `decryptPayload` / `extractToken` / `validateTokenFormat` |
29
29
  | shell(I/O あり) | `getTokenFilePath` / `readCliTokenSync` / `tryReadCliToken` / `loadPortalToken` / `savePortalToken` / `deletePortalToken` |
30
30
  | 艦隊の資格の門(1.1.0・n=2607) | `checkFleetGate` / `isAllowedForScope` / `getFleetEntitlementFilePath` / `getFleetGateLogPath` / `formatFleetGateLogLine` / `fleetGateRefusalMessage` / `parseFleetEntitlementCache` / `isFleetEntitlementFresh` |
31
+ | 交換済み JWT の機体共有キャッシュ(1.2.0・n=2774) | `getExchangedJwt` / `evictExchangedJwt` / `exchangedJwtCacheKey` / `getExchangedJwtCacheDir` / `parseCachedExchangedJwt` / `isCachedExchangedJwtUsable` |
32
+ | 機体の鍵・登録・札の延長(1.3.0・n=3918) | `startDeviceRegistration` / `pollDeviceRegistration` / `awaitDeviceApproval` / `saveApprovedDevice` / `loadOrCreateDeviceKey` / `readDeviceKey` / `signWithDeviceKey` / `devicePollMessage` / `tokenRefreshMessage` / `readDeviceId` / `saveDeviceId` / `ensureFreshCliToken` / `readFreshCliToken` / `needsRefresh` / `parseExpiresAt` |
31
33
 
32
34
  ### 艦隊の資格の門(`checkFleetGate`)
33
35
 
@@ -41,6 +43,27 @@ const maybe = tryReadCliToken();
41
43
  - `scope`(既定 `"fleet"`): `fleet`=cancelled なら止める(orchestrator・scheduler)。`member`=cancelled でも理由が `no_seat`・`rank_below_min` なら止めない(routine=初伝にも配る。席が丸ごと落ちた・退会・期限切れの時だけ止める)。キャッシュと口は 1 つのまま
42
44
  - throw しない。止めた時の文面は `fleetGateRefusalMessage(reason)`(固定)
43
45
 
46
+ ### 交換済み JWT の機体共有キャッシュ(`getExchangedJwt`)
47
+
48
+ 艦隊の手足(monster-orchestrator・scheduler・routine)が `POST {bluelamp}/auth/exchange` で取る 10 分の JWT を、**期限まで機体で共有**する。
49
+ 以前はプロセスのメモリにだけ持っていたので、発火のたびに立つ fire.js は毎回 exchange し、毎時 :00 に揃っていた(2026-10-09 実測 636 件・p50 6.3 秒)。
50
+
51
+ - 置き場 `~/.bluelamp/jwt-cache/<鍵>.json`(ファイル 600・ディレクトリ 700。`BLUELAMP_JWT_CACHE_DIR` で差し替え=消費側のテスト用)。自分だけが読めるファイルしか信じない
52
+ - 鍵は「口・mcp_id・scope の組・CLI トークンの指紋」の sha256。JWT は `bl:mcp_id` を持つので MCP をまたいで共有しない。CLI トークンはファイルに書かない
53
+ - 取り直すのは期限の 60 秒前から。同時に切れた N プロセスは `<鍵>.json.lock`(`O_EXCL`)を取った 1 本だけが撃ち、他はファイルを待つ(既定 5 秒・過ぎたら自分で撃つ)。30 秒より古いロックは持ち主が死んだと読んで払う
54
+ - 受け手が 401 を返したら `evictExchangedJwt(key, 拒まれた JWT)` → もう一度 `getExchangedJwt`(中身が同じ JWT のときだけ消す)
55
+ - キャッシュの不調(読めない・書けない・ロックが取れない)は exchange に倒す。exchange の失敗だけは throw のまま返す
56
+
57
+ ### 機体の鍵と札の延長(`ensureFreshCliToken`・1.3.0)
58
+
59
+ CLI の札を機体に結び、短い期限(1 時間)を裏で延ばす(箱 n=3918)。
60
+
61
+ - 機体の鍵: Ed25519。`~/.musuhi/device-key.pem`(PKCS8・0600・フォルダ 0700)。OS ユーザー 1 つに鍵 1 つ。⛔ OS の鍵束に置かない(SSH・launchd の便で開かない)。権限が開いた鍵は使わない(`DeviceKeyError`)
62
+ - 口座ごとの機体の id: `~/.musuhi/device.json`(0600)`{ "<user_id>": "<device_id>" }`
63
+ - 札の置き場(`portal-token.enc`)は 0600・一時ファイル→rename で書く。機体に結んだ札は `expiresAt`(epoch ms)と `deviceId` を持つ。持たない札は古い札(延ばさない)
64
+ - `ensureFreshCliToken()`: 残り 15 分を切った札だけ、`~/.musuhi/token-refresh.lock`(O_EXCL・2 分より古いロックは払う)を取れた 1 手が `POST {base}/cli/token/refresh {device_id, ts, nonce, signature}`(`X-CLI-Token`=今の札)を撃つ。署名の文は `bluelamp-token-refresh\n{device_id}\n{ts}\n{nonce}`(ts は epoch 秒・署名は base64)。他の手は最大 5 秒待って置き場を読む。throw しない。断られても置き場を消さない
65
+ - 読み手は 401 を受けたら `readFreshCliToken()` で読み直して 1 回だけ撃ち直す(普段の要求は今までどおり札だけ・1 回ごとに署名しない)
66
+
44
67
  ## 制約
45
68
 
46
69
  - **node 標準モジュール以外に依存しない**(VSCode 拡張が webpack で bundle するため)。