Even G2でXのHome、Following、Bookmarks、threadを読み、Like、Repost、Bookmarkを切り替えるTypeScript製アプリです。
X API keyは使いません。Mac上のログイン済みブラウザをtwitter_api_safe_relayが操作し、このプロジェクトのgatewayが必要な結果だけを固定DTOへ変換してEven G2へ渡します。XがpromotedMetadataを付けたtimeline entryは広告として正規化前に除外します。
Important
twitter_api_safe_relayをインターネットへ直接公開しないでください。Tunnelへ接続するのはBearer認証付きgatewayだけです。write操作はLike、Repost、Bookmarkの有効化・解除だけに制限し、投稿、返信、Follow、削除などは公開しません。
Even G2 / iPhone WebView
│ authenticated GET / PUT / DELETE
▼
Doge gateway :8787
│ allowlist済み10操作だけ
▼
twitter_api_safe_relay :6900 (localhost only)
│
▼
ログイン済みX browser profile
apps/g2— Even Hub SDK、576×288 glasses UI、投稿者icon・投稿画像、iPhone companion UIapps/gateway— Hono/Node.js gateway、固定reaction routes、Xレスポンス正規化、avatar・投稿画像proxypackages/contracts— frontendとgatewayで共有するZod schemascripts— relay catalog同期とproduction origin生成
起動直後はHome、Following、Bookmarksのview選択画面です。
| 場所 | 入力 | 動作 |
|---|---|---|
| view選択 | scroll | Home / Following / Bookmarks選択 |
| view選択 | tap | 選択したviewを開く |
| view選択 | double tap | Dogeを終了 |
| 投稿view | 上スワイプ | 本文の続き / 読了後に次の投稿 |
| 投稿view | 下スワイプ | 本文の前ページ / 前の投稿 |
| 投稿view | tap | 右側のaction menuを開く |
| 投稿view | double tap | view選択へ戻る |
| action menu | scroll | Like / Repost / Bookmark / thread |
| action menu | tap | 選択を実行し、成功後menuを閉じる |
G2のscrollはcontentを引っ張るnatural/inverse方式ではありません。投稿viewでは、進みたい方向へ指をslideします(下方向で次の本文page/post、上方向で前の本文page/post)。view選択とaction menuはG2 native listのscrollに従います。
長い本文はG2の実フォント幅に合わせてページ分割し、文字を省略しません。G2画面内には常設の操作guideを置かず、その領域も本文とview選択listに使います。画像付きpostでは、本文末尾の同じtimeline frame内に1〜4枚を縦横比を維持したgridとして埋め込みます。本文が短いほど画像領域を大きく取り、各slotは取得中だけ独立したloading placeholderを表示します。Galleryは画像だけを最大表示する別modeです。動画・animated GIFは静止posterを再生マーク付きで表示し、動画データの取得・再生は行いません。
取得済みmediaのBlobとG2用に変換済みのPNG tileはWebView memory内のLRU cacheへ短期間保持します。同じpostやGallery画像へ戻った場合はnetwork取得・decode・再変換を省略します。ただしEven SDKには眼鏡側の画像cacheをIDで再利用するAPIがないため、page rebuild後のBLE再送自体は必要です。icon、投稿者画像、投稿画像はG2 bridgeへencoded PNG/JPEGのbyte列として順番に渡します。高速にpostやviewを切り替えた場合、古いavatar取得結果は破棄し、最新renderだけをimage containerへ反映します。
apps/g2/public/doge-icon.png はiPhone側のweb app iconと、view選択後の初回loading画面で使います。特定の写真やTwitter/Xの旧logoを複製しない、このproject用のoriginal illustrationです。Even Hubの掲載iconには同じファイルを手動でuploadしてください。詳細は同directoryの doge-icon.LICENSE.md を参照してください。
- Node.js 22以降
- Even Hub対応のEven Appとpairing済みEven G2
- live X利用時のみ、別途起動した
twitter_api_safe_relayとログイン済みbrowser profile
mockにはXへの接続が不要です。
npm install
npm run devgatewayはhttp://127.0.0.1:8787、Viteはhttp://127.0.0.1:5173で起動します。iPhone側画面はVite URL、Even Hub simulatorにも同じVite URLを指定してください。
検証とpackaging:
npm run verify
npm run pack:g2生成物はapps/g2/doge.ehpkです。.ehpkとbuild成果物はGit管理しません。
このprojectはHeliumとは別のPlaywright Chromium profileを使います。初回だけ専用browserでXへログインし、以後はvar/relay-profileに保存されたsessionを使います。profileにはX cookieとLocal Storageが入るため、directory全体をGitから除外しています。
最初に専用Chromiumをinstallします。
npx playwright install chromiumSafe Relayと専用browserを起動します。
npm run relay:login表示されたChromiumでXへログインし、https://x.com/homeが表示されたらwindowを開いたままにします。別terminalで状態を確認できます。
npm run relay:check続いて現在のquery IDとfeature flagsを取得し、gatewayをrelay modeで起動します。
npm run relay:sync
X_SOURCE=relay npm run dev:gateway
npm run dev:g2Safe Relayは127.0.0.1:6900、gatewayは127.0.0.1:8787を使います。3000はこのMacのTraumaが使用中なので使いません。Safe Relayのbrowserを閉じるかCtrl-Cするとrelayも停止します。
relay catalogはX側の変更に追従するversion lockです。var/requests.ndjsonはaccount由来のIDやcursorを含み得るため、.gitignoreで除外しています。gatewayは起動後もcatalogを読み直すため、同期後の再buildは不要です。
Safe Relayへログイン済みで、npm run buildとnpm run relay:syncが完了していれば、実Xデータ用の一時previewを起動できます。
npm run preview:liveこのcommandは256-bitの一時tokenを生成し、Bearer認証を必須にしたscoped gatewayと使い捨てCloudflare Quick Tunnelを起動します。tokenはterminalへ出力せず、権限600の一時QR画像にだけ埋め込みます。URL fragmentはCloudflareへ送られず、Even WebViewがsession内でtokenを取り込み、API requestのAuthorization headerへ変換します。Ctrl-CでgatewayとTunnelを終了し、QR画像を削除してtokenを失効させます。
Quick Tunnelは実機開発専用で、可用性保証や固定URLはありません。本番運用ではdoge.h1ka.ruのnamed Tunnelを使います。
本番originはhttps://doge.h1ka.ruです。manifestとfrontend環境を再生成する場合:
npm run configure:origin -- https://doge.h1ka.ru
npm run build
npm run pack:g2:productionこのコマンドはGit管理外の次の2ファイルを生成します。
apps/g2/.env.production.local— frontendのgateway originapps/g2/app.production.json— Even Hub network whitelist
Cloudflare Tunnelで公開するのはgatewayの127.0.0.1:8787だけです。Safe Relayのport 6900はTunnelへ接続しません。
初回にDoge access keyを生成し、クリップボードへコピーします。key自体はterminalへ表示せず、var/doge-access-keyへ権限600で保存します。
npm run production:keyEven AppのDoge画面でGateway URLとkeyを入力し、Save and test connectionを押します。認証確認に成功した設定だけをEven App SDKのdevice local storageへ保存し、次回起動時は保存済み設定で自動接続します。従来のWebView local storageにあるkeyも初回に移行します。keyを変更するときだけ新しい値を入力し、空欄なら保存済みkeyを維持します。Forget access keyでdevice storageからも削除できます。X cookieはMacから出ません。
Gateway URLはphone画面で編集できますが、Even Hubのnetwork permissionはbuild時の完全一致origin allowlistです。wildcardは使えないため、公開buildはhttps://doge.h1ka.ruだけに接続できます。別originを運用する場合は、そのoriginをmanifestへ含めた別buildを作るか、公開版の後続versionでallowlistへ追加してください。
Safe Relayが起動中であることを確認して本番gatewayとnamed Tunnelを起動します。
npm run production:startGateway sourceを更新してnpm run buildした場合、実行中のproduction:startも再起動してください。Node processは起動時に読み込んだdist/server.jsを使い続けるため、buildだけではprofileなどの新routeは反映されません。
外出中もMacの電源・ネット接続、Safe Relay、production:startを維持してください。現在の構成ではiPhoneとG2だけでXへ直接接続するわけではなく、自宅Macがbackendです。
推奨はBeta buildです。Betaは公開Store審査なしで、自分のEven accountだけをtester groupへ追加できます。公開版と同じlifecycleで動き、phone lockやbackground遷移も含めた外出利用を試せます。Private buildも自分だけでinstallできますが、配布できず、lifecycleは公開版と完全には同じではありません。
npm run verify && npm run pack:g2:production- Even HubでBeta tester groupを作り、自分のEven account emailを追加
apps/g2/doge.ehpkをbuildとしてuploadし、そのgroupへpush- iPhoneのEven Appで
Me → Beta tester → Doge → Install - glasses homeからDogeを起動し、phone画面でaccess keyを一度だけpair
公開versionはrelease後に同じversion番号で差し替えられないため、Betaでnative settings復元、background復帰、実機profile表示を確認してから提出します。掲載iconにはrepository同梱のoriginal assetを使えます。
公開審査が発生するのはStoreへsubmissionする段階です。詳細はEven Hub公式のBeta testingとPrivate testingを参照してください。
- public client routeは認証確認、timeline、thread、profile、avatar・投稿画像の
GETと、Like、Repost、Bookmark専用のPUT/DELETEのみ - feed、cursor、post IDをschema検証
- relay base URLはloopback HTTPのみ許可
- relay operationは4つのread操作とFavorite/Unfavorite、Create/Delete Retweet、Create/Delete Bookmarkの6つだけ
- XのGraphQL errorをHTTP 200でも失敗として扱う
- upstream timeout 15秒、response上限5 MB
- avatar proxyは
pbs.twimg.com/profile_imagesのHTTPS画像だけを許可し、redirect禁止、5秒timeout、512 KB上限、画像content-type必須 - 投稿画像proxyは
pbs.twimg.comの写真および動画posterの固定pathだけを許可し、redirect禁止、5秒timeout、4 MB上限、JPEG/PNG/WebPのcontent-typeとfile signature一致を必須化。video.twimg.comやMP4/HLSは取得しない - responseは固定DTOへ変換し、X cookieや内部headerをclientへ返さない
- production responseは
no-store、security headers付き - 本番gatewayはBearer token必須。tokenを
.ehpkへ埋め込まず、各iPhoneで一度だけpair - installed WebViewのoriginが変わってもBearer認証を必須にしたままCORS responseを返す
.env*、Cloudflare credentials、browser profile、relay catalog、build output、coverage、.ehpkを除外しています。X cookieやCloudflare tokenをrepository内へ置かないでください。
GNU Affero General Public License v3.0 only。Trauma projectと同じライセンスです。