Skip to content

Repository files navigation

Doge for Even G2

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 UI
  • apps/gateway — Hono/Node.js gateway、固定reaction routes、Xレスポンス正規化、avatar・投稿画像proxy
  • packages/contracts — frontendとgatewayで共有するZod schema
  • scripts — relay catalog同期、preview、maintainer用deployment

操作

起動直後は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)

mockにはXへの接続が不要です。

npm install
npm run dev

gatewayはhttp://127.0.0.1:8787、Viteはhttp://127.0.0.1:5173で起動します。iPhone側画面はVite URL、Even Hub simulatorにも同じVite URLを指定してください。

初回はphone画面のGateway settingsへhttp://127.0.0.1:8787と43文字の開発用access keyを入力してSave and test connectionを押します。認証に成功した組だけが保存され、以後は自動復元されます。

検証とpackaging:

npm run verify
npm run pack:g2

生成物はapps/g2/doge.ehpkです。.ehpkとbuild成果物はGit管理しません。

live X relayへ切り替える

このprojectはHeliumとは別のPlaywright Chromium profileを使います。初回だけ専用browserでXへログインし、以後はvar/relay-profileに保存されたsessionを使います。profileにはX cookieとLocal Storageが入るため、directory全体をGitから除外しています。

最初に専用Chromiumをinstallします。

npx playwright install chromium

Safe 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:g2

Safe 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は不要です。

認証付き実機preview

Safe Relayへログイン済みで、npm run buildnpm 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へ送られません。phone画面でQRと同じHTTPS originをGateway URLとして保存・検証すると、以後のAPI requestへAuthorization headerが付与されます。Ctrl-CでgatewayとTunnelを終了し、QR画像を削除してtokenを失効させます。

Quick Tunnelは実機開発専用で、可用性保証や固定URLはありません。本番運用ではdoge.h1ka.ruのnamed Tunnelを使います。

Public buildとGateway pairing

public buildは特定のGateway URLを含みません。Even Hub manifestはuserがphone画面で選ぶ任意のHTTPS serviceへの通信を許可し、Dogeは次の順序でpairingします。

  1. userがGatewayのHTTPS URLと43文字のaccess keyを入力
  2. DogeがBearer key付きでGET /api/v1/sessionを呼ぶ
  3. 認証済みDoge Gateway protocol v1応答を確認
  4. 成功したURLとkeyの組だけをEven App SDKのdevice local storageへ保存
  5. 次回起動時は保存値を復元し、同じsession endpointで再確認

未設定時にtimelineやmedia requestは送信しません。Gateway URLの入力draftは接続成否にかかわらずEven SDK storageへ別keyで保持し、失敗後や再起動後も入力欄へ復元します。access keyは認証成功後に専用のEven SDK storage keyへ保存し、書き戻し確認が成功してから接続済みとして扱います。起動時はdevice storageの復元が完了するまで設定操作を無効化し、保存済みURL+keyがあれば再入力なしで起動します。keyを変更するときだけ新しい値を入力し、空欄なら保存済みkeyを維持します。password欄が空でもSaved access key is activeと表示されていればkeyは非表示のまま有効です。Forget access keyはURLを入力欄へ残したままkeyを削除します。旧WebView storageまたは旧形式に保存済みのURL+keyも初回にdevice storageへ移行します。

公開packageを作る場合:

npm run verify
npm run pack:g2:production

apps/g2/app.production.jsonはGit管理し、network whitelistをhttps://に固定します。build時環境変数、配布package、frontend sourceのいずれにも運用者のGateway URLやaccess keyを埋め込みません。Gateway実装の互換contractはdocs/gateway-protocol.mdです。

各userはこのcontractを実装したGatewayをHTTPSで公開し、phone画面からpairingします。X cookieはGateway hostから出ません。

Maintainer deployment

このrepositoryのmaintainer用named Tunnelは現在https://doge.h1ka.ruを使います。これはserver運用scriptの設定であり、Doge public buildのdefault接続先ではありません。

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:key

Even AppのDoge画面でhttps://doge.h1ka.ruとkeyを入力し、Save and test connectionを押します。

Safe Relayが起動中であることを確認して本番gatewayとnamed Tunnelを起動します。

npm run production:start

Gateway sourceを更新してnpm run buildした場合、実行中のproduction:startも再起動してください。Node processは起動時に読み込んだdist/server.jsを使い続けるため、buildだけではprofileなどの新routeは反映されません。

外出中もMacの電源・ネット接続、Safe Relay、production:startを維持してください。現在の構成ではiPhoneとG2だけでXへ直接接続するわけではなく、自宅Macがbackendです。

Private buildとBeta build

推奨はBeta buildです。Betaは公開Store審査なしで、自分のEven accountだけをtester groupへ追加できます。公開版と同じlifecycleで動き、phone lockやbackground遷移も含めた外出利用を試せます。Private buildも自分だけでinstallできますが、配布できず、lifecycleは公開版と完全には同じではありません。

  1. npm run verify && npm run pack:g2:production
  2. Even HubでBeta tester groupを作り、自分のEven account emailを追加
  3. apps/g2/doge.ehpkをbuildとしてuploadし、そのgroupへpush
  4. iPhoneのEven AppでMe → Beta tester → Doge → Install
  5. glasses homeからDogeを起動し、phone画面でaccess keyを一度だけpair

公開versionはrelease後に同じversion番号で差し替えられないため、Betaでnative settings復元、background復帰、実機profile表示を確認してから提出します。掲載iconにはrepository同梱のoriginal assetを使えます。

公開審査が発生するのはStoreへsubmissionする段階です。詳細はEven Hub公式のBeta testingPrivate testingを参照してください。

Security defaults

  • 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を返す

Gitに入れないもの

.env*、Cloudflare credentials、browser profile、relay catalog、build output、coverage、.ehpkを除外しています。X cookieやCloudflare tokenをrepository内へ置かないでください。

License

GNU Affero General Public License v3.0 only。Trauma projectと同じライセンスです。

Releases

Packages

Contributors

Languages