@www.hyperlinks.space/program-kit 1.2.91881 → 18.18.18

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.
@@ -1,345 +0,0 @@
1
- # Security Model: Telegram-First Wallet
2
-
3
- ## Goals
4
-
5
- - Create wallets inside the Telegram Mini App.
6
- - Let users access the **same wallet** from:
7
- - Telegram on any device.
8
- - Other platforms (web / desktop / native) via **Login with Telegram**.
9
- - Keep custody with the **user** (mnemonic as ultimate root of trust), while using Telegram features to improve UX.
10
-
11
- This document describes the current high-level design; it is not a formal audit.
12
-
13
- ---
14
-
15
- ## Key Concepts & Storage
16
-
17
- **Key terms**
18
-
19
- - **Mnemonic (seed phrase)**: Ultimate secret that controls the wallet. If lost, funds are lost; if leaked, funds are at risk.
20
- - **Wallet master key**: Device-specific key derived from the mnemonic and stored only on that device.
21
- - **Wallet seed cipher**: Encrypted blob that contains the wallet seed (or a seed-derived secret), usable only together with the wallet master key.
22
- - **Telegram CloudStorage**: Per-user, per-bot key–value store synced via Telegram servers. Suitable for non-secret data or ciphertext.
23
- - **Telegram SecureStorage**: Per-device secure storage, backed by iOS Keychain / Android Keystore, intended for small sensitive values like tokens and keys (see [Mini Apps docs](https://core.telegram.org/bots/webapps#securestorage)).
24
-
25
- We **never store raw mnemonics** on our backend or in Telegram CloudStorage.
26
-
27
- ---
28
-
29
- ## I. Wallet Creation in Telegram
30
-
31
- Flow when a user creates a wallet for the first time in the Telegram Mini App on a device:
32
-
33
- 1. **Generate mnemonic on the device**
34
- - The Mini App generates a new mnemonic locally (client-side).
35
- - The user is shown the mnemonic once and asked to write it down for lifelong backup.
36
-
37
- 2. **Derive wallet master key (device-local)**
38
- - From the mnemonic, the app derives a **wallet master key** (e.g. via a BIP-style KDF / HKDF).
39
- - This master key is stored **only** in Telegram `SecureStorage` on that device.
40
- - Because `SecureStorage` is backed by **Keychain / Keystore**, the key is:
41
- - Encrypted at rest.
42
- - Bound to this device + Telegram app.
43
-
44
- 3. **Create and store wallet seed cipher (cloud)**
45
- - The app creates a **wallet seed cipher**:
46
- - `seed_cipher = Encrypt(wallet_seed_or_root, wallet_master_key)`.
47
- - `seed_cipher` is stored in Telegram `CloudStorage` under this bot for this user.
48
- - CloudStorage contains **only ciphertext**, never raw seed or keys.
49
-
50
- 4. **Persist wallet mapping in our backend**
51
- - Our backend stores a record like:
52
- - `telegram_username` (Telegram username, treated as unique in our system),
53
- - `wallet_address` (public address),
54
- - metadata (creation time, network, etc.).
55
- - No mnemonics or master keys are saved server-side.
56
-
57
- **Properties**
58
-
59
- - The **first Telegram device** has everything needed to use the wallet:
60
- - Master key in `SecureStorage`.
61
- - Seed cipher in `CloudStorage`.
62
- - If the app is reinstalled on the **same device**, we can:
63
- - Recover the master key from `SecureStorage` (if Telegram restores it).
64
- - Decrypt the seed cipher for a smooth UX without re-entering the mnemonic.
65
- - If `SecureStorage` is wiped, the user must re-enter the mnemonic.
66
-
67
- ---
68
-
69
- ## II. New Telegram Device Authorization
70
-
71
- When the same user opens the Mini App on **another Telegram client** (new phone/tablet):
72
-
73
- 1. **Detect existing wallet**
74
- - Using `initData` / backend lookup, we see that this `telegram_username` already owns at least one `wallet_address`.
75
-
76
- 2. **Check device SecureStorage**
77
- - On this **new device**, `SecureStorage` is empty for our bot (no wallet master key yet).
78
- - We may still see the `seed_cipher` in `CloudStorage` (it is synced across user devices), but without a master key it is useless.
79
-
80
- 3. **Ask user for mnemonic to authorize this device**
81
- - The Mini App prompts the user to enter their mnemonic.
82
- - From the mnemonic we derive a **new device-specific wallet master key** and store it in this device’s `SecureStorage`.
83
- - Optionally, we re-encrypt and update the `seed_cipher` for this device; but the canonical root of trust remains the mnemonic.
84
-
85
- **Result**
86
-
87
- - Each Telegram device gains access only after the user proves knowledge of the mnemonic once.
88
- - Loss of a device does **not** affect other devices, as long as the mnemonic is safe.
89
-
90
- ---
91
-
92
- ## III. Other Platforms (Outside Telegram)
93
-
94
- For platforms outside Telegram (web, desktop app, native mobile app not running inside Telegram), we:
95
-
96
- 1. **Authenticate via Telegram**
97
- - Use **Telegram Login for Websites** (see [login widget docs](https://core.telegram.org/widgets/login)) in a browser or embedded WebView.
98
- - The login widget:
99
- - Shows a Telegram-branded authorization UI where the user confirms sharing their Telegram identity with our site.
100
- - After success, returns a signed payload containing `id`, `first_name`, `last_name`, `username`, `photo_url`, `auth_date`, and `hash`.
101
- - On our backend we verify the `hash` using HMAC-SHA-256 with the SHA-256 of the bot’s token as a key, as described in the official docs.
102
- - This works on:
103
- - **Desktop browsers** (Chrome, Firefox, Safari, Edge, …),
104
- - **Mobile browsers** on **iOS and Android**,
105
- - In-app WebViews (e.g. inside a native app’s web login screen).
106
-
107
- > For pure native iOS/Android apps, we typically embed a small web login page (with the widget) or open the system browser, then pass the verified Telegram identity back into the app.
108
-
109
- 2. **Lookup existing wallet**
110
- - Once Telegram login succeeds, we resolve `telegram_username` in our DB.
111
- - If there is an associated `wallet_address`, we know this user already owns a wallet.
112
-
113
- 3. **Authorize the device with the mnemonic**
114
- - Because we are **outside Telegram**, we do not have Telegram’s `SecureStorage`.
115
- - We therefore ask the user to **enter the mnemonic** to authorize this new platform.
116
- - The app derives a **local wallet master key** from the mnemonic and stores it using the platform’s secure storage:
117
- - iOS: Keychain
118
- - Android: Keystore
119
- - Desktop: DPAPI / Keychain / Keyring, depending on OS
120
- - Web: WebCrypto + IndexedDB (with the usual caveats around user clearing storage).
121
-
122
- **Result**
123
-
124
- - Telegram login is used purely as an **identity layer** (“who is this user?”).
125
- - The **mnemonic** is still required to authorize a completely new platform.
126
- - Our backend never receives the raw mnemonic; only derived public keys / addresses.
127
-
128
- ---
129
-
130
- ## IV. Android / iOS (Single App, Telegram‑Centric Flow)
131
-
132
- We ship **one app** (the current Expo app at repository root) to iOS/Android stores. On mobile, the app acts as a shell that:
133
-
134
- - Explains why wallet creation happens in Telegram.
135
- - Sends the user into the Telegram Mini App to actually create the wallet.
136
- - Then uses **Telegram Login for Websites** + our backend to recognize the user and their wallet.
137
-
138
- ### Database: user identification (Telegram username–centric)
139
-
140
- In the backend we maintain a `users` table (or equivalent) with at least:
141
-
142
- - `telegram_username` – Telegram username, treated as unique in our system.
143
- - `wallet_address` – current wallet address owned by this username.
144
-
145
- The mobile app never needs to store the mnemonic or keys; it only needs the `telegram_username` and `wallet_address` retrieved from the backend after Telegram login.
146
-
147
- ### A. New user on iOS/Android (no wallet yet)
148
-
149
- 1. **App start → “Log in with Telegram” screen**
150
- - Shown when there is no linked `telegram_username` in local app storage.
151
- - Content:
152
- - Title: “Create your wallet in Telegram”.
153
- - Short text summarizing benefits:
154
- - “Wallets are created **inside Telegram**, we never see your seed phrase.”
155
- - “Your keys are stored using Telegram **SecureStorage + CloudStorage** on your devices.”
156
- - “You can access the same wallet from Telegram, web, and this app.”
157
- - UI:
158
- - Primary button: **“Open Telegram to create wallet”**.
159
- - Secondary button (dimmed / smaller): **“I already created my wallet in Telegram”** (goes to login widget, see flow B).
160
- - Action on primary:
161
- - Deep link to Mini App, e.g. `https://t.me/<bot_username>?startapp=wallet_onboarding`.
162
-
163
- 2. **Telegram Mini App: wallet creation (same as section I)**
164
- - Generate mnemonic, show/confirm it.
165
- - Derive wallet master key → store in `SecureStorage`.
166
- - Encrypt wallet seed → store cipher in `CloudStorage`.
167
- - Call backend `register_wallet(telegram_username, wallet_address)` which:
168
- - Creates a new `users` row.
169
- - Stores `telegram_username`, `wallet_address`.
170
- - Show a completion screen:
171
- - “Your wallet is ready in Telegram.”
172
- - Instruction: “Return to the app and tap ‘I already created my wallet’ to connect.”
173
-
174
- 3. **Back in iOS/Android app → “Log in with Telegram” (Telegram login widget)**
175
- - User returns to the app and taps **“I already created my wallet in Telegram”** (or simply “Log in with Telegram”).
176
- - App opens an embedded WebView or browser page with **Telegram Login for Websites**:
177
- - Widget asks the user to confirm sharing their Telegram identity.
178
- - After success, backend receives a signed payload and validates it as in section III.
179
- - Backend extracts `username` from the payload and looks up `telegram_username` in `users`:
180
- - If found, returns `{ telegram_username, wallet_address, ... }` to the app.
181
- - If not found (edge case: user logged in before creating wallet), the app can:
182
- - Show “No wallet yet, please create it in Telegram first” and show the **Open Telegram** button again.
183
-
184
- 4. **App state after linking**
185
- - The app stores `telegram_username` and `wallet_address` in its local storage.
186
- - It can now:
187
- - Display balances / history via backend or blockchain APIs.
188
- - For signing, either:
189
- - Redirect back to Telegram Mini App on an action (“Sign in Telegram”), or
190
- - In a future advanced flow, allow the user to import mnemonic locally (explicit, non‑default).
191
-
192
- ### B. Existing Telegram wallet user installing iOS/Android app
193
-
194
- For a user who already has a Telegram wallet (created earlier in the Mini App):
195
-
196
- 1. **App start → same “Log in with Telegram” screen**
197
- - The screen is identical, but the text emphasizes:
198
- - “If you already created your wallet in Telegram, just log in below.”
199
- - Action:
200
- - User taps **“I already created my wallet in Telegram”** / **“Log in with Telegram”**.
201
-
202
- 2. **Telegram Login for Websites**
203
- - Same widget flow as in A.3.
204
- - Backend verifies the payload and looks up `telegram_username` in `users`:
205
- - If a row exists → return `{ telegram_username, wallet_address }` to the app.
206
- - If no row exists → the app suggests:
207
- - “We don’t see a wallet yet. Please open Telegram to create one.” and shows the **Open Telegram to create wallet** button.
208
-
209
- 3. **After login**
210
- - The app saves `telegram_username` + `wallet_address` locally and shows the wallet UI.
211
- - No mnemonic is ever handled by the app in this default flow.
212
-
213
- **Key points of this model**
214
-
215
- - There is **one app** (repository root), not a separate companion; mobile just has a Telegram-centric onboarding path.
216
- - All wallet creation and key material remain in the Telegram Mini App and its SecureStorage/CloudStorage environment.
217
- - The iOS/Android app uses:
218
- - **Telegram Login for Websites** to learn “who is this user?” (via `username`),
219
- - The backend’s `users` table (`telegram_username`, `wallet_address`) to link that identity to a wallet.
220
- - Users who don’t want to connect Telegram can only use future “local‑only” features if we add them explicitly; the default path is Telegram‑first.
221
-
222
- ---
223
-
224
- ## V. Serverless Transaction Flow & Bot‑Based Confirmation
225
-
226
- We assume a **serverless backend** (e.g. Vercel / Cloudflare / Lambda functions) with:
227
-
228
- - Stateless HTTP handlers (API routes).
229
- - A persistent datastore (serverless Postgres / KV / Dynamo, etc.) for:
230
- - `users` (`telegram_username`, `wallet_address`, …).
231
- - `pending_transactions` (id, telegram_username, payload, status, timestamps).
232
-
233
- All **signing** happens on the client side (Telegram Mini App or other wallet environment); serverless functions never hold private keys.
234
-
235
- ### 1. Initiating a transaction (any client)
236
-
237
- From any frontend (Telegram Mini App, web, iOS/Android app):
238
-
239
- 1. Client sends a request to a serverless endpoint, e.g. `POST /api/tx/create`:
240
- - Body includes:
241
- - `telegram_username` (or derived from Telegram auth),
242
- - transaction intent (action type, amount, asset, destination, etc.).
243
- 2. Serverless handler:
244
- - Validates the request and user identity.
245
- - Creates a `pending_transactions` row with:
246
- - `id` (e.g. UUID or short code),
247
- - `telegram_username`,
248
- - serialized transaction payload (what needs to be signed),
249
- - status = `pending`.
250
- - Uses the **Telegram Bot API** to send a push message to the user:
251
- - “New action requires confirmation: <summary>”.
252
- - Inline button: **“Review & confirm”** linking to:
253
- - Recommended: Mini App deep link, e.g.\
254
- `https://t.me/<bot_username>?startapp=tx_<id>`
255
- - Optionally, a web URL, e.g.\
256
- `https://app.hyperlinks.space/tx/<id>`.
257
-
258
- ### 2. Confirmation page (Telegram Mini App)
259
-
260
- When the user taps the button in the bot message and opens the Mini App:
261
-
262
- 1. The Mini App receives `start_param = tx_<id>` via `initData`.
263
- 2. It calls a serverless endpoint, e.g. `GET /api/tx/<id>`:
264
- - Backend verifies the request using `initData`/`hash` and checks that:
265
- - `pending_transactions.id` exists,
266
- - `telegram_username` from DB matches the Mini App user,
267
- - status is `pending`.
268
- - Backend returns the transaction details (read‑only).
269
- 3. Mini App shows a **confirmation UI**:
270
- - Clear summary: amounts, assets, destination, fees, network.
271
- - Buttons: **Confirm** / **Reject**.
272
-
273
- ### 3. Signing & broadcasting (inside Telegram)
274
-
275
- On **Confirm** from the Mini App:
276
-
277
- 1. Mini App reconstructs/derives the transaction to be signed using:
278
- - The locally available wallet (seed/master key in `SecureStorage`).
279
- - The payload from `pending_transactions`.
280
- 2. Mini App signs the transaction **locally** using the wallet key.
281
- 3. Mini App sends a request to a serverless endpoint, e.g. `POST /api/tx/<id>/complete` with:
282
- - `id`,
283
- - signed transaction payload (or only the resulting transaction hash if the client broadcasts itself),
284
- - confirmation that user accepted.
285
- 4. Serverless handler:
286
- - Verifies that the caller is the right `telegram_username` and that tx is still `pending`.
287
- - Either:
288
- - Broadcasts the signed transaction to the chain (if backend has RPC access and broadcasting doesn’t leak secrets), **or**
289
- - Simply records the fact that this transaction was confirmed and uses a separate service / worker to broadcast.
290
- - Updates `pending_transactions.status` to `confirmed` (or `failed` with reason).
291
-
292
- The originating client (web / mobile app) can poll or subscribe for status changes on `pending_transactions` to reflect completion.
293
-
294
- ### 4. Optional: web‑only confirmation page
295
-
296
- For users who click a **web URL** from the bot instead of the Mini App:
297
-
298
- - The confirmation page:
299
- - Uses **Telegram Login for Websites** to authenticate the user (retrieve `username`).
300
- - Loads the `pending_transactions` row by `id` + `telegram_username`.
301
- - Shows the same confirmation UI.
302
- - For signing, we recommend **redirecting back to Telegram** for actual key usage:
303
- - On **Confirm**, the web page can:
304
- - Either deep‑link into the Mini App with the same `tx_<id>`, or
305
- - Ask the user to enter the mnemonic or connect an external wallet (advanced, non‑default).
306
- - This keeps the **self‑custodial key** anchored in the Mini App by default, while still allowing purely web‑based flows where the user explicitly opts into entering their mnemonic or linking another wallet.
307
-
308
- ### Serverless guarantees
309
-
310
- - All backend logic is stateless across requests; long‑lived data lives in `users` and `pending_transactions`.
311
- - No private keys or mnemonics are ever stored or derived in serverless functions.
312
- - Bot pushes and confirmation flows are coordinated exclusively via:
313
- - Telegram Bot API (for notifications),
314
- - Telegram Mini App (for secure signing with SecureStorage),
315
- - Telegram Login (for identity on web / mobile).
316
-
317
- This model keeps transaction approvals **user‑driven and key‑local** (inside Telegram or another explicit wallet) while still fitting neatly into a serverless architecture.
318
-
319
- ---
320
-
321
- ## Security Properties
322
-
323
- - **Self-custody:**
324
- - The mnemonic is the **only ultimate key**; if the user keeps it offline and safe, they retain full control over funds.
325
- - Neither our backend nor Telegram can unilaterally move funds without the mnemonic / keys.
326
-
327
- - **Device-local keys:**
328
- - Each device has its own **wallet master key** stored in that device’s secure storage (Telegram SecureStorage or OS keystore).
329
- - Compromise of one device does **not** automatically compromise others.
330
-
331
- - **Cloud data is ciphertext only:**
332
- - `Wallet Seed Cipher` in CloudStorage is encrypted with a master key that lives only in secure storage on a device.
333
- - An attacker with only CloudStorage access cannot derive the mnemonic.
334
-
335
- - **Cross-platform restore requires mnemonic:**
336
- - Any **new environment** (new Telegram device with empty SecureStorage or any non-Telegram platform) requires the mnemonic once.
337
-
338
- ---
339
-
340
- ## Limitations & Notes
341
-
342
- - If the **mnemonic is lost**, there is no recovery (by design) – it is the self-custodial root.
343
- - If a device with a master key is compromised, an attacker can act as the owner from that device until the user moves funds to a new wallet.
344
- - Telegram `SecureStorage` is documented for **iOS and Android**; behavior on other Telegram clients may differ.
345
- - Telegram Login for Websites is an **authentication mechanism only** – it does not give access to keys or the mnemonic itself, and cannot replace the mnemonic for wallet authorization.
@@ -1,63 +0,0 @@
1
- # Raw timing estimate
2
-
3
- **Purpose:** Rough “time till app finish” and **MVP launch** based on commit history, plans, and the front app.
4
-
5
- **Important:** The app will not be sent to platforms (store/listing) before it is fully finished. So **MVP launch = full product launch**, and the realistic estimate is **3 months**.
6
-
7
- ## Transfer layout to repository root (TypeScript) — build on new architecture
8
-
9
- The goal is **not** to finish the Flutter app in `front/`. The **Flutter** app in `front/` is the **reference layout** (web pages, flows, structure). The work is to **transfer that layout and flows into the repository root app in TypeScript** and make everything work there, with **security plan compliance** (see `docs/security_plan.md`). The production Mini App is the TS app at repository root (Expo/React), backed by the same repo’s API and DB.
10
-
11
- ### Pages to implement in repository root (TS) — from `front/` reference
12
-
13
- - **Bootstrap** – initData auth, config from API, wallet warm-up.
14
- - **Main** – home / hub after bootstrap.
15
- - **Send** – send flow (security plan: pending tx, confirmations).
16
- - **Swap** – swap flow (rate state, rotation, etc. from root plan).
17
- - **Wallets** – list/create/manage; wallet creation and nickname (security plan: mnemonic flow, device auth, wallet registration).
18
- - **Apps** – apps design, brand book alignment.
19
- - **AI** – AI & search bar; response formatting; scrolling fix; wired to `app` AI routes.
20
- - **Get** – get flow.
21
- - **Trade** – trade page.
22
- - **Creators** – creators content.
23
- - **Mnemonics** – mnemonic display/backup (security plan).
24
- - **Wallet panel** – wallet panel page.
25
-
26
- ### Services / wiring in repository root (TS)
27
-
28
- | Concern | Role | Backend / compliance |
29
- |--------|------|-----------------------|
30
- | **Auth** | Telegram initData auth (e.g. app’s telegram/init endpoint). | initData validation and user upsert (Phase 0–1). |
31
- | **AI** | Chat with AI via same-origin `/api/ai` (or app’s AI route). | Already in `app`; ensure config, keys, and error handling in prod. |
32
- | **Wallet** | Mnemonic generation, storage, key derivation (client-side). | Security plan: SecureStorage/CloudStorage, wallet registration (`POST /api/wallet/register`), device auth flow; optional TonConnect later. |
33
-
34
- ### API surface (in repository root)
35
-
36
- - **`/api/telegram`** (or init) – initData validation, user upsert.
37
- - **`/api/ai`** – AI proxy; used by the TS app.
38
-
39
- All of the above must work end-to-end in the TS app and stay within the security model (initData-only auth, no raw secrets to server, wallet registration and pending tx where applicable).
40
-
41
- ---
42
-
43
- ## Short and Medium term backlogs
44
-
45
- - **`app/short_term_backlog.md`** (3 items): Response formatting, app theme, AI & search bar.
46
- - **Root `medium_term_backlog.md`** (backlog): Header indent bug, rotation on swap, edit-interval healthcheck, ticker link, scrolling in AI widget, bot parts isolation, ld timeout, “AI bd”, hovers, languages, favicon, wallet creation & nickname, jetton lockup, theme in Chrome, “Bd”, wallet creation, apps design, plan users BD for search, brand book, fixes, starting rewards, feed item types, PAY BY QR, swap long rate state, swaps, tokens.
47
-
48
- **Security:** All features above are delivered **with security plan compliance** (Phase 0–2: initData validation, wallet creation and registration, device auth, pending transactions, TonConnect if in scope, etc.). No “MVP without security”; the launch build is compliant.
49
-
50
- ---
51
-
52
- ## Time estimate
53
-
54
- Completion order and stages:
55
-
56
- | Stage | Scope | Estimate | Notes |
57
- |-------|--------|----------|--------|
58
- | **1** | **Security plan implementation** | **~3 weeks** | Phase 0 (validation, DB), Phase 1 (wallet creation, device auth, register), Phase 2 (pending tx, confirmations). |
59
- | **2** | **App (TS) – pages & UX** | **~3 weeks** | Transfer layout from `front/` and implement all pages in repository root app (send, swap, wallets, apps, AI, get, trade, creators, mnemonics, wallet panel); layout and navigation solid. |
60
- | **3** | **App (TS) – services & wiring** | **~3 weeks** | Auth, AI, and wallet flows in repository root app; backend alignment; errors and edge cases. |
61
- | **4** | **Integration, testing, finishing** | **~3 weeks** | E2E, platform checks, store/listing readiness, app/plan and root plan items (formatting, theme, bugs, features). |
62
-
63
- **MVP launch (full finish, platform-ready):** **~3 months** (12 weeks).
@@ -1,69 +0,0 @@
1
- # TMA logo bar jump on input focus – fix
2
-
3
- ## Problem
4
-
5
- When the user focuses the AI & Search input in the Telegram Mini App, the keyboard opens and the logo bar jumps (shifts down then up). The whole column (logo bar + main + bottom bar) was also scrollable.
6
-
7
- **Cause:** Viewport height shrinks when the keyboard opens; if root/body height follows that, the layout reflows and the host may scroll the window → logo bar moves. Reacting on every browser resize (e.g. Visual Viewport) caused intermediate reflows before the final state.
8
-
9
- ## What we tried (dismissed or replaced)
10
-
11
- | Approach | Outcome |
12
- |----------|--------|
13
- | **Position fixed for logo bar** | Reverted. Did not fix the shift inside TMA. |
14
- | **Root height from `viewport.stableHeight()`** (pin layout to stable height so it doesn’t change on keyboard) | Not used. We chose “single update when TMA reports” instead. |
15
- | **Visual Viewport API** (`window.visualViewport` resize + `body.style.height` + `--vh` + `scrollTo(0,0)` on every resize) | Replaced. Caused intermediate reflows; keyboard open triggered multiple resize events before the final size. |
16
- | **Scroll lock only** (prevent window scroll) | Necessary but not sufficient on its own; layout still shifted. |
17
-
18
- ## Solution
19
-
20
- Use **only the TMA viewport API**. When the keyboard opens, change nothing until TMA sends **viewport_changed**; then apply the new height and scroll reset in one turn.
21
-
22
- ### 1. TMA viewport (Telegram.tsx)
23
-
24
- - After `viewport.mount()`:
25
- - **`viewport.bindCssVars()`** – SDK sets/updates `--tg-viewport-height`. Layout height is driven by this.
26
- - **`on("viewport_changed", handler)`** – Run **`window.scrollTo(0, 0)` only when `payload.is_state_stable`** (or `payload.isStateStable`). Do not reset scroll during drag or animation.
27
- - **Window scroll lock** – `window.addEventListener("scroll", …)` → if `scrollY > 0` then `scrollTo(0, 0)`.
28
- - **viewport-fit=cover** on the viewport meta (iOS).
29
-
30
- ### 2. CSS (global.css)
31
-
32
- - **html**: `height: 100%`, `overflow: hidden`, `overscroll-behavior: none`.
33
- - **body**, **#root** / **[data-expo-root]**: `height: var(--tg-viewport-height, 100%)`, `min-height: var(--tg-viewport-height, 100%)`, `overflow: hidden` (body also `overscroll-behavior: none`).
34
-
35
- ### 3. Layout
36
-
37
- - **Root View** (_layout.tsx): `overflow: "hidden"`, flex column.
38
- - **Logo bar** (GlobalLogoBar): `flexShrink: 0`.
39
- - **Main**: `flex: 1`, `minHeight: 0`.
40
-
41
- ### 4. TMA (telegramWebApp.ts)
42
-
43
- - **readyAndExpand()**: `expand()`, `setHeaderColor("#000000")`, `setupSwipeBehavior({ allow_vertical_swipe: false })`, `disableVerticalSwipes?.()`.
44
-
45
- ## viewport_changed payload
46
-
47
- | Field | Type | Description |
48
- |-------|------|-------------|
49
- | `height` | number | Viewport height. |
50
- | `width` | number (optional) | Viewport width. |
51
- | `is_expanded` | boolean | Viewport expanded. |
52
- | `is_state_stable` | boolean | State stable (no change in the next moment). |
53
-
54
- Only call `scrollTo(0, 0)` when **is_state_stable** is true. Support both `is_state_stable` and `isStateStable` (bridge payload shape).
55
-
56
- ## Summary: keep vs dismiss
57
-
58
- **Keep in the stack:**
59
-
60
- - TMA-only viewport logic: `viewport.bindCssVars()`, `on("viewport_changed", …)` with **is_state_stable** check before `scrollTo(0, 0)`, window scroll lock, viewport-fit=cover.
61
- - CSS: `--tg-viewport-height` for body and #root, overflow/overscroll locked on html/body and root.
62
- - Root `overflow: "hidden"` and logo bar `flexShrink: 0`.
63
- - readyAndExpand (expand, setHeaderColor, setupSwipeBehavior, disableVerticalSwipes).
64
-
65
- **Dismiss as try-outs:**
66
-
67
- - Any use of **Visual Viewport API** for TMA (resize/scroll listeners, `body.style.height`, `--vh`) — replaced by TMA viewport + bindCssVars + viewport_changed.
68
- - **Position fixed** for the logo bar — reverted.
69
- - **stableHeight**-based root height — not used; we use bindCssVars (current height) and rely on **is_state_stable** to avoid resetting scroll until the viewport is stable.
package/docs/update.md DELETED
@@ -1,205 +0,0 @@
1
- # Instant App Updates Plan
2
-
3
- This plan defines how to deliver near-instant updates for the app with minimal user friction, while keeping binary releases for native-level changes.
4
-
5
- ## 1) Update Strategy (Two Lanes)
6
-
7
- - **Lane A: OTA updates (default, fast path)**
8
- - Use Expo EAS Update to deliver JavaScript/TypeScript and asset changes.
9
- - No reinstall required.
10
- - Target latency: minutes or less after publish.
11
- - **Lane B: Binary releases (slow path)**
12
- - Use installer/store release only when native/runtime changes are required.
13
- - Triggered through `releases/**` and GitHub Release workflow.
14
-
15
- Decision rule:
16
-
17
- - JS/UI/business logic/assets changes -> OTA
18
- - Native dependency/plugin/permission/runtime changes -> binary release
19
-
20
- ## 2) Current Project Baseline
21
-
22
- Already configured:
23
-
24
- - `app.json` has `updates.url`.
25
- - `app.json` has `runtimeVersion.policy = appVersion`.
26
- - `eas.json` has channels:
27
- - preview profile -> `main`
28
- - production profile -> `production`
29
-
30
- Implication:
31
-
32
- - Binaries built on a channel only receive updates from that same channel and compatible runtime version.
33
-
34
- ## 3) Target Architecture for "Instant" Updates
35
-
36
- 1. Developer merges code to branch.
37
- 2. CI publishes OTA update to EAS (`main` or `production` branch/channel).
38
- 3. App receives update check trigger (foreground/resume and optional push signal).
39
- 4. App fetches update, then applies at safe point (next app restart or immediate reload based on policy).
40
-
41
- For binary-only updates:
42
-
43
- 1. `releases/builder/build_MMDDYYYY_HHMM/` (or Forge under `releases/forge/...`) changes.
44
- 2. GitHub workflow dedupes and creates Release if new.
45
- 3. Workflow calls `POST /api/releases` webhook.
46
- 4. Backend notifies clients that binary update is available.
47
-
48
- ## 4) Server and CI Setup
49
-
50
- ### 4.1 GitHub Secrets
51
-
52
- Add repository secrets:
53
-
54
- - `EXPO_TOKEN` (required for EAS CLI in CI)
55
- - `RELEASE_WEBHOOK_URL` (for binary-release notification)
56
- - `RELEASE_WEBHOOK_TOKEN` (for endpoint auth)
57
-
58
- ### 4.2 OTA Workflow (GitHub Actions)
59
-
60
- Create workflow `.github/workflows/eas-update.yml`:
61
-
62
- - Trigger:
63
- - `push` to selected branches (`main`, optionally `release/*`)
64
- - Optional `workflow_dispatch`
65
- - Path filters to avoid unnecessary publishes (example):
66
- - include app source and config paths
67
- - exclude `releases/**`
68
- - Steps:
69
- 1. checkout
70
- 2. setup node
71
- 3. install deps (`npm ci` in repository root)
72
- 4. publish OTA:
73
- - branch `main` for preview/internal
74
- - branch `production` for production
75
- 5. output EAS update group id and URL
76
-
77
- Recommended publish command:
78
-
79
- - `npx eas update --branch <branch> --non-interactive --message "<commit message>"`
80
-
81
- ### 4.3 Binary Workflow (Already Planned)
82
-
83
- Keep `releases/**` workflow with dedupe:
84
-
85
- - If release exists, skip.
86
- - If new, create GitHub Release and upload assets.
87
- - Call `POST /api/releases`.
88
-
89
- ## 5) App Runtime Behavior
90
-
91
- Implement client update behavior with `expo-updates`:
92
-
93
- 1. On app start and on foreground:
94
- - call `checkForUpdateAsync()`
95
- 2. If update exists:
96
- - call `fetchUpdateAsync()`
97
- 3. Apply policy:
98
- - silent + apply on next launch (default safe)
99
- - or prompt user and reload now (`reloadAsync()`)
100
-
101
- Suggested policy:
102
-
103
- - **Critical fixes:** prompt and reload now
104
- - **Normal updates:** fetch silently and apply next launch
105
-
106
- Minimum checks:
107
-
108
- - On cold start
109
- - On foreground if last check older than threshold (for example 10 minutes)
110
-
111
- ## 6) Push-Accelerated Detection (Optional but Preferred)
112
-
113
- OTA itself is already fast, but to reduce time further:
114
-
115
- - Add backend push trigger when CI publishes OTA.
116
- - App receives push and immediately runs update check.
117
-
118
- Channels you can use:
119
-
120
- - Expo Push Notifications
121
- - Firebase Cloud Messaging
122
- - WebSocket/SSE (if app has persistent session)
123
-
124
- Fallback remains foreground polling.
125
-
126
- ## 7) Versioning and Compatibility Rules
127
-
128
- With `runtimeVersion.policy = appVersion`:
129
-
130
- - OTA is only delivered to binaries with matching `expo.version`.
131
- - When native changes occur:
132
- - bump app version
133
- - ship new binary
134
- - publish OTA for that new runtime afterward
135
-
136
- Team rule:
137
-
138
- - Do not bump app version for pure JS hotfixes unless intentionally creating a new runtime.
139
-
140
- ## 8) Security and Reliability
141
-
142
- - Protect `/api/releases` using token and optional HMAC.
143
- - Make webhook handler idempotent by `release_id`.
144
- - Retry webhook delivery in workflow with backoff.
145
- - Log update lifecycle:
146
- - CI publish success/failure
147
- - release webhook accepted/rejected
148
- - app check/fetch/apply events (client telemetry)
149
-
150
- ## 9) Rollout Plan
151
-
152
- Phase 1 - Staging:
153
-
154
- 1. Implement OTA workflow for `main` channel only.
155
- 2. Add app-side check/fetch on startup + foreground.
156
- 3. Test with internal build on `main`.
157
-
158
- Phase 2 - Production:
159
-
160
- 1. Add production publish workflow/manual gate.
161
- 2. Add push-accelerated trigger.
162
- 3. Monitor crash-free sessions and update adoption rate.
163
-
164
- Phase 3 - Hardening:
165
-
166
- 1. Add kill switch/rollback procedure.
167
- 2. Add release dashboard with latest OTA and binary status.
168
-
169
- ## 10) Operational Playbook
170
-
171
- ### Publish OTA update
172
-
173
- From repository root:
174
-
175
- - `npx eas update --branch main --message "fix: ..."`
176
- - for production:
177
- - `npx eas update --branch production --message "fix: ..."`
178
-
179
- ### Publish binary update
180
-
181
- 1. Create `releases/builder/build_MMDDYYYY_HHMM/` with installer at root and supporting files under `dev/`.
182
- 2. Push changes.
183
- 3. Workflow creates (or skips duplicate) GitHub Release and calls `/api/releases`.
184
-
185
- ### Rollback OTA
186
-
187
- - Republish a known-good commit to the same branch/channel with a new OTA message.
188
- - If needed, temporarily disable update checks via remote config flag.
189
-
190
- ## 11) Acceptance Criteria
191
-
192
- - OTA updates reach active users without reinstall for JS/UI changes.
193
- - Median time from CI publish to client fetch is within target SLA.
194
- - Binary updates only occur for native/runtime changes.
195
- - Duplicate `release_id` never creates duplicate GitHub Release.
196
- - `/api/releases` accepts only authenticated requests.
197
- - App always has fallback update detection even if push signal fails.
198
-
199
- ## 12) Next Implementation Tasks
200
-
201
- 1. Create `api/releases.ts` endpoint.
202
- 2. Add `.github/workflows/eas-update.yml`.
203
- 3. Add client update service wrapper (`check/fetch/apply` policy).
204
- 4. Add docs section in `build_and_install.md` linking OTA vs binary rules.
205
- 5. Add monitoring events for update checks and apply outcomes.