Skip to content

feat: publish the mobile app's own terms and privacy policy - #18

Merged
azuchi merged 2 commits into
chaintope:mainfrom
Yamaguchi:feat/add_legal_docs_for_the_mobile_app
Sep 8, 2026
Merged

azuchi merged 2 commits into
chaintope:mainfrom
Yamaguchi:feat/add_legal_docs_for_the_mobile_app

Conversation

@Yamaguchi

@Yamaguchi Yamaguchi commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Adds a terms of service and a privacy policy for the mobile app, published from
docs/app/. They are separate documents from the extension's.

Background

The iOS app is being submitted to the App Store
(chaintope/tapylet-for-mobile#21), and App Review requires the privacy policy
handed to Apple to apply to the app being reviewed (guideline 5.1.1). Both of
this repository's documents define "the Service" as the Chrome extension.

PR #16 answered that by widening the extension's documents to cover all three
platforms at once. This branch takes the other route: the mobile app gets
documents of its own, and the extension's stay as they are. The two are not the
same product — different storage, different permissions, different distribution
— and one document that has to qualify every clause per platform serves neither
reader well.

The app's repository is private and so cannot have GitHub Pages, while the store
listings need a public URL. The documents are therefore published from here,
under docs/app/, alongside the min-version.json the app already reads.

No manifest for these

docs/legal.json, the re-consent banner and BUNDLED_LEGAL_DOCS are the
extension's. The mobile app does not record which version a user agreed to and
does not read the manifest, so these documents carry none: the version in effect
is whatever docs/app/<doc>.html points at, and a revision reaches every user
as soon as it is deployed.

That has a consequence worth stating, and the README now states it: swapping the
file announces nothing. Section 8 of the terms undertakes to announce a revision
before it takes effect, so a revision that changes rights or obligations has to
be carried by an announcement on the company site, or by a release of the app
that asks again on screen. Wording and typos need neither.

What the documents say

Both are based on version 2.1 as drafted in PR #16, narrowed to iOS and Android
and checked against the app's code rather than against the extension's text.

Subject What it says
Storage App-private storage; the wallet data encrypted under a password-derived key and kept in the iOS Keychain, or in the Android Keystore-backed store
Backup The wallet data is out of iCloud backup and not restorable to another device; on Android none of the app's data is backed up. Accounts, history and settings can reach iCloud
Camera Used to read address QR codes. Frames are processed on the device, nothing is stored or sent, and the address can be typed instead
External requests The Tapyrus node/API, the Token Registry, min-version.json, and the token icons — which come from whatever host the issuer registered, outside our control
Links out The registry's GitHub issue form, the block explorer, the store page for an update, and issuer URLs from token metadata
Deleting the app What iOS leaves in the Keychain is never read again; creating or restoring a wallet overwrites it
Network Switched in the settings screen, mainnet by default
Forced update The app may refuse to run below the minimum supported version (terms, section 7)

The terms do not link to the privacy policy: a link would take the policy into
the contract, and the two checkboxes on the welcome screen would stop meaning
anything.

Changes to the files

File Change
docs/app/terms/v1.0.html New
docs/app/privacy/v1.0.html New
docs/app/terms.html, docs/app/privacy.html New. Send the version-less URL to v1.0
docs/index.html Lists the extension's documents and the app's, each labelled
test/mobileLegalDocs.test.ts New
README.md How docs/app/ is versioned, and what a revision has to do beyond deploying

Effect on users

None on the extension. docs/legal.json, docs/terms/, docs/privacy/ and
src/extension/legal.ts are untouched, so nobody is asked to agree to anything
again.

The mobile app is not yet released. Once chaintope/tapylet-for-mobile#23 is
merged it links to app/terms.html and app/privacy.html; until this branch is
deployed those URLs are 404, so merge and deploy this first.

How to verify

pnpm test   # 127 tests, including test/mobileLegalDocs.test.ts

After deploying:

for u in app/terms.html app/privacy.html app/terms/v1.0.html app/privacy/v1.0.html; do
  printf "%-26s %s\n" "$u" \
    "$(curl -s -o /dev/null -w '%{http_code}' https://chaintope.github.io/tapylet/$u)"
done
# all 200

curl -s https://chaintope.github.io/tapylet/app/privacy.html | grep -o 'privacy/v[0-9.]*\.html'
# => privacy/v1.0.html

Then open https://chaintope.github.io/tapylet/ and check that both pairs are
reachable and that the back link on each document returns to it.

Related

Notes

- The mobile app gets its own documents, separate from the extension's
- They live under docs/app/, next to min-version.json, because the app's
  repository is private and cannot have GitHub Pages
- The app records no consent version, so these carry no manifest: the
  version in effect is whatever docs/app/<doc>.html points at
- README says what a revision that changes rights still has to do, since
  swapping the file announces nothing

@azuchi azuchi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

文書・テスト・README とも、モバイルアプリの実装(tapylet-for-mobile を clone して確認)と突き合わせてレビューしました。npx tsc --noEmit パス、pnpm test 127 件全パスを実測しています。要対応 1 件のほかは正確で、この PR 自体はマージ可能と考えます。

要対応: Android のバックアップ記述と実装の不一致

プライバシーポリシー §5.1 は「Androidでは、(ウォレットデータを)端末のバックアップの対象から除外しています」「アカウント情報、トランザクション履歴および設定は…Androidでは、バックアップの対象に含めていません」と断言していますが、モバイル側の app.json に android.allowBackup の設定が無く、バックアップ除外ルールも見当たりません。Expo の既定は allowBackup: true のため、現状のビルドでは:

  • AsyncStorage 上のアカウント情報・履歴・設定は Android Auto Backup に平文で含まれ、別端末にも復元されます(記述と正反対)
  • ウォレットデータの暗号文(expo-secure-store の SharedPreferences)もバックアップに含まれ得ます。Keystore の鍵は端末外に出ないため他端末で復号はできず「復元されない」は実効的に保たれますが、「対象から除外」は文字どおりには成立しません

対処は二択です。

  1. モバイル側に expo.android.allowBackup: false を足す(推奨。1 行でポリシー本文がそのまま真になり、ウォレットとして安全側)
  2. 文書側を「バックアップに含まれる場合でも、鍵が無ければ復号できません」という言い回しに改める

アプリ未リリース・#23 も未マージの今なら手戻りなく揃えられます。1 を採る場合、この PR は現状のままマージで問題ありません(アプリのリリース前に mobile 側の設定が入ることが条件)。

確認できた点

  • 同意フロー: WelcomeScreen は規約・ポリシー個別の 2 チェックボックスで、両方チェックまで作成/復元が無効。規約 §1 および「規約がポリシーへリンクしない」設計(テストで固定)と整合
  • 「モバイルは manifest を読まない」は正しい: アプリに legal.json の取得コードは存在せず、§2.4 の通信先一覧(min-version.json のみ)は正確
  • ネットワーク: DEFAULT_NETWORK = "mainnet"・設定画面で切替 — 規約 §2 と一致
  • 保存と暗号化: PBKDF2 導出鍵の AES-256-GCM + expo-secure-store(WHEN_UNLOCKED_THIS_DEVICE_ONLY)— §2.2 と iOS の「他の端末に復元されない」記述と一致
  • カメラ: expo-camera を QR 読取文言付きで宣言、マイク無効。位置情報・連絡先系のパーミッションも無し — §2.1/§2.5 と一致
  • アイコン取得: メタデータの icon/image URL を直接取得(sanitizeImageUrl ガード付き)— 「発行者登録の、当社管理外サーバー」という開示は正確
  • 強制アップデート: 規約 §7 は version-gate 設計(fail-open)と一致
  • マージ順: #23 が参照 URL を …/tapylet/app/ へ切り替える内容であることを diff で確認。「本 PR を先にデプロイ」は正しい手順
  • 拡張機能側(docs/legal.json・src/extension/legal.ts 等)への変更ゼロ
  • test/mobileLegalDocs.test.ts が「規約にポリシーを取り込まない」という法的な設計判断まで固定しているのは良い作り

微小な点(対応任意)

  • リダイレクトページのコメントが「Update the two links below」ですが、実際は 3 箇所です(meta refresh・canonical・本文リンク。テストが不一致を検出するので実害なし)
  • Notes の「legal.json は発効日を過ぎた 2.0 を告知したまま」は正しい現状認識で、別 PR での対処に同意します

- The comment said two; the refresh, the canonical and the body link
  each name the version in effect
@Yamaguchi
Yamaguchi force-pushed the feat/add_legal_docs_for_the_mobile_app branch from 9875ff5 to e23c66a Compare September 8, 2026 07:06
@Yamaguchi

Copy link
Copy Markdown
Contributor Author

2点対応しました。

Android のバックアップ

選択肢1(expo.android.allowBackup: false を足す)を採りました。chaintope/tapylet-for-mobile#23 に 0412f02 として対応してます。

  • app.json の expo.android.allowBackup を false に。@expo/config-plugins の AllowBackup.js:26 が config.android?.allowBackup ?? true と読むため、未指定では true になっていました。ご指摘のとおりです
  • src/storage/adapters/expo.ts の DEFENSE IN DEPTH コメントに、端末外へ出さない仕組みが OS で違うことを書きました。iOS は WHEN_UNLOCKED_THIS_DEVICE_ONLY、Android は allowBackup: false です。app.json は JSON でコメントを書けないため、理由はコード側に置いています

これでプライバシーポリシー §5.1 の記述がそのまま真になるので、文書側の変更はありません。

補足です。allowBackup: false が止めるのはクラウドバックアップで、Android 12 以降の端末間ダイレクト転送は android:dataExtractionRules の管轄です。こちらは expo-secure-store の config plugin が入れる規則(<include domain="sharedpref" path="."/> と <exclude domain="sharedpref" path="SecureStore"/>)が効いており、SecureStore は転送対象外、AsyncStorage も database ドメインなので include に入らず転送されません。ウォレットデータもアカウント情報も端末から出ない状態です。

リダイレクトページのコメント

e23c66a で3箇所(meta refresh・canonical・本文リンク)が同時に動く旨に書き換えました。

拡張機能側の docs/terms.html と docs/privacy.html も同じ文言だったので、あわせて直しています。同じ構造で同じ誤りなので、この PR に含めました。

@azuchi
azuchi merged commit c5c72f4 into chaintope:main Sep 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants