@www.hyperlinks.space/program-kit 1.2.3

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.
Files changed (87) hide show
  1. package/README.md +53 -0
  2. package/api/ai.ts +111 -0
  3. package/api/base.ts +117 -0
  4. package/api/blockchain.ts +58 -0
  5. package/api/bot.ts +19 -0
  6. package/api/ping.ts +41 -0
  7. package/api/releases.ts +162 -0
  8. package/api/telegram.ts +65 -0
  9. package/api/tsconfig.json +17 -0
  10. package/app/_layout.tsx +135 -0
  11. package/app/ai.tsx +39 -0
  12. package/app/components/GlobalBottomBar.tsx +447 -0
  13. package/app/components/GlobalBottomBarWeb.tsx +362 -0
  14. package/app/components/GlobalLogoBar.tsx +108 -0
  15. package/app/components/GlobalLogoBarFallback.tsx +66 -0
  16. package/app/components/GlobalLogoBarWithFallback.tsx +24 -0
  17. package/app/components/HyperlinksSpaceLogo.tsx +29 -0
  18. package/app/components/Telegram.tsx +648 -0
  19. package/app/components/telegramWebApp.ts +359 -0
  20. package/app/fonts.ts +12 -0
  21. package/app/index.tsx +102 -0
  22. package/app/theme.ts +117 -0
  23. package/app.json +60 -0
  24. package/assets/icon.ico +0 -0
  25. package/assets/images/favicon.png +0 -0
  26. package/blockchain/coffee.ts +217 -0
  27. package/blockchain/router.ts +44 -0
  28. package/bot/format.ts +143 -0
  29. package/bot/grammy.ts +52 -0
  30. package/bot/responder.ts +620 -0
  31. package/bot/webhook.ts +262 -0
  32. package/database/messages.ts +128 -0
  33. package/database/start.ts +133 -0
  34. package/database/users.ts +46 -0
  35. package/docs/ai_and_search_bar_input.md +94 -0
  36. package/docs/ai_bot_messages.md +124 -0
  37. package/docs/backlogs/medium_term_backlog.md +26 -0
  38. package/docs/backlogs/short_term_backlog.md +39 -0
  39. package/docs/blue_bar_tackling.md +143 -0
  40. package/docs/bot_async_streaming.md +174 -0
  41. package/docs/build_and_install.md +129 -0
  42. package/docs/database_messages.md +34 -0
  43. package/docs/fonts.md +18 -0
  44. package/docs/releases.md +201 -0
  45. package/docs/releases_github_actions.md +188 -0
  46. package/docs/scalability.md +34 -0
  47. package/docs/security_plan_raw.md +244 -0
  48. package/docs/security_raw.md +345 -0
  49. package/docs/timing_raw.md +63 -0
  50. package/docs/tma_logo_bar_jump_investigation.md +69 -0
  51. package/docs/update.md +205 -0
  52. package/docs/wallets_hosting_architecture.md +257 -0
  53. package/eas.json +47 -0
  54. package/eslint.config.js +10 -0
  55. package/fullREADME.md +159 -0
  56. package/global.css +67 -0
  57. package/npmReadMe.md +53 -0
  58. package/package.json +214 -0
  59. package/scripts/load-env.ts +17 -0
  60. package/scripts/migrate-db.ts +16 -0
  61. package/scripts/program-kit-init.cjs +58 -0
  62. package/scripts/run-bot-local.ts +30 -0
  63. package/scripts/set-webhook.ts +67 -0
  64. package/scripts/test-api-base.ts +12 -0
  65. package/telegram/post.ts +328 -0
  66. package/tsconfig.json +17 -0
  67. package/vercel.json +7 -0
  68. package/windows/after-sign-windows-icon.cjs +13 -0
  69. package/windows/build-layout.cjs +72 -0
  70. package/windows/build-with-progress.cjs +88 -0
  71. package/windows/build.cjs +2247 -0
  72. package/windows/cleanup-legacy-appdata-installs.ps1 +91 -0
  73. package/windows/cleanup-legacy-windows-shortcuts.ps1 +46 -0
  74. package/windows/cleanup.cjs +200 -0
  75. package/windows/embed-windows-exe-icon.cjs +55 -0
  76. package/windows/extractAppPackage.nsh +150 -0
  77. package/windows/forge/README.md +41 -0
  78. package/windows/forge/forge.config.js +138 -0
  79. package/windows/forge/make-with-stamp.cjs +65 -0
  80. package/windows/forge-cleanup.cjs +255 -0
  81. package/windows/hsp-app-process.ps1 +63 -0
  82. package/windows/installer-hooks.nsi +373 -0
  83. package/windows/product-brand.cjs +42 -0
  84. package/windows/remove-orphan-uninstall-registry.ps1 +67 -0
  85. package/windows/run-installed-with-icon-debug.cmd +20 -0
  86. package/windows/run-win-electron-builder.cjs +46 -0
  87. package/windows/updater-dialog.html +143 -0
@@ -0,0 +1,34 @@
1
+ ### Database refactor and extensions (plan)
2
+
3
+ **Goal**
4
+
5
+ - DB bootstrap in `app/database`; keep `users`, `wallets`, `pending_transactions` as-is.
6
+ - One minimal AI table shared by bot and TMA; one user identity: `user_telegram` (bot: `ctx.from.username`; TMA: initData `user.username`). No Chat table; TMA-only users use `type = 'app'`.
7
+
8
+ ---
9
+
10
+ **messages** (single table, not implemented yet)
11
+
12
+ - `id` (PK, BIGSERIAL) — same as other tables (e.g. wallets).
13
+ - `created_at` (TIMESTAMPTZ, NOT NULL DEFAULT now()).
14
+ - `user_telegram` (TEXT, FK → `users(telegram_username)`).
15
+ - `thread_id` (BIGINT) — bot: Telegram `message_thread_id` (0 = default); TMA: app-chosen (e.g. 0).
16
+ - `type` (TEXT) — `'bot'` | `'app'`.
17
+ - `role` (TEXT) — `'user'` | `'assistant'` | `'system'`.
18
+ - `content` (TEXT).
19
+ - `telegram_update_id` (BIGINT, nullable) — bot only; NULL for TMA. Source: Telegram webhook payload (`ctx.update.update_id` in bot handler). Used for: (1) unique constraint → one row per update per thread, no double-reply; (2) before send, check max = our update_id → if not, abort (avoids mixed replies in serverless). TMA doesn’t need it: requests come from the app (HTTP), not Telegram’s update stream, so there is no update_id; TMA concurrency is a separate concern (e.g. client or request-scoped).
20
+
21
+ **Thread key** — `(user_telegram, thread_id, type)` together identify one conversation. Example: user "alice", topic 0, bot = one thread; same user, topic 5, bot = another thread; same user, app = TMA thread.
22
+
23
+ **Index** — `(user_telegram, thread_id, type, created_at)` so we can quickly get "all messages in this thread, ordered by time" (for building chat history for AI).
24
+
25
+ **Unique for bot** — `(user_telegram, thread_id, type, telegram_update_id)` where `telegram_update_id IS NOT NULL`. Meaning: in a given thread, each Telegram update_id may appear at most once. So we can’t insert two rows for the same update (e.g. duplicate webhook or two serverless instances); the second insert fails, that handler skips. Only the first insert “owns” the reply.
26
+
27
+ **Bot: no message mixing (messages-only)**
28
+
29
+ 1. Insert user message with `telegram_update_id`. On unique violation → skip.
30
+ 2. Before each send: if `MAX(telegram_update_id)` for thread ≠ our update_id → abort.
31
+ 3. No extra tables.
32
+
33
+ **Migrations**
34
+
package/docs/fonts.md ADDED
@@ -0,0 +1,18 @@
1
+ # Fonts (Expo web / TMA)
2
+
3
+ ## What the bottom bar asks for today
4
+
5
+ - **Name in code:** **`Aeroport`** (geometric / grotesk-style sans — the brand face used elsewhere in the product).
6
+ - **Reality in this repo:** There is **no** Aeroport font file under `app/assets/` and **no** `@font-face` rule, so **the browser cannot load “Aeroport”**. It falls back — in Telegram’s WebView that often looks like a **serif** (“antiqua”), not a grotesk.
7
+
8
+ ## What you see after the fix
9
+
10
+ - **`app/fonts.ts`** exports **`WEB_UI_SANS_STACK`**: **`Aeroport` first** (for when files exist), then **`ui-sans-serif, system-ui, …, sans-serif`** so text always uses a **modern sans** until Aeroport is bundled.
11
+
12
+ ## How to use real Aeroport (optional)
13
+
14
+ 1. Add licensed `.otf`/`.ttf` files, e.g. `app/assets/fonts/Aeroport-Regular.otf`.
15
+ 2. Register with **`expo-font`** in the root layout, e.g. `useFonts({ Aeroport: require("./assets/fonts/Aeroport-Regular.otf") })`.
16
+ 3. Add **`@font-face`** in `global.css` if you prefer pure CSS loading on web (paths must match the exported static asset URL).
17
+
18
+ Until then, the **stack** in `global.css` + `GlobalBottomBarWeb` keeps the UI **grotesk-like** via system fonts.
@@ -0,0 +1,201 @@
1
+ # GitHub Releases Automation Plan (Final)
2
+
3
+ This document defines the production plan for release detection, deduped GitHub Release publishing, and near-instant app update signaling.
4
+
5
+ ## Goals
6
+
7
+ - Build the Windows installer on GitHub Actions (`windows-latest`) with `npm run build:win` (no `.exe` committed to git).
8
+ - Publish the installer to a GitHub Release only when that release tag does not already exist.
9
+ - Notify an app-update service immediately after a new release is published.
10
+ - Keep app-side update detection near-instant using push notification plus fallback polling.
11
+
12
+ ## Source of Truth
13
+
14
+ - The **GitHub Release tag** is the release identity (`release_id`), for example `build_03252026_1929`.
15
+ - Locally, `windows/cleanup.cjs` moves the built installer to `app/releases/builder/build_MMDDYYYY_HHMM/HyperlinksSpaceProgramInstaller_<stamp>.exe` (other artifacts under `dev/`). In CI you can set `RELEASE_BUILD_ID` to match a chosen tag, or leave it unset so the folder name comes from build time.
16
+
17
+ ## Workflow Trigger
18
+
19
+ - **Manual only:** `workflow_dispatch` (Actions → “Windows release (CI build)”).
20
+ - Optional input **release_id** (`build_MMDDYYYY_HHMM`). If empty, the tag is taken from the build output folder after `cleanup.cjs` runs.
21
+
22
+ Example trigger:
23
+
24
+ ```yaml
25
+ on:
26
+ workflow_dispatch:
27
+ inputs:
28
+ release_id:
29
+ description: "Optional tag, e.g. build_03252026_1929"
30
+ required: false
31
+ default: ""
32
+ ```
33
+
34
+ ## Dedupe Rules (No Duplicate Releases)
35
+
36
+ 1. Run `npm run build:win` (or use optional `RELEASE_BUILD_ID` so the output folder matches the intended tag).
37
+ 2. Resolve `release_id` from the optional input or from `releases/builder/build_*/HyperlinksSpaceProgramInstaller_*.exe`.
38
+ 3. Check whether GitHub Release/tag already exists for that `release_id`.
39
+ 4. If it exists:
40
+ - Exit successfully (`0`)
41
+ - Do not upload assets
42
+ - Do not send webhook notification
43
+ 5. If it does not exist:
44
+ - Create GitHub Release/tag
45
+ - Upload all files from that folder as release assets
46
+ - Continue to webhook notification
47
+
48
+ Recommended extra safety:
49
+
50
+ - Use workflow `concurrency` keyed by `release_id` to avoid race conditions.
51
+
52
+ ## Endpoint Contract
53
+
54
+ - Webhook endpoint path: `app/api/releases.ts`
55
+ - Route URL: `POST /api/releases`
56
+ - Purpose: accept release-published events from GitHub Actions and fan out update notifications to app clients.
57
+
58
+ ## Security
59
+
60
+ - GitHub Actions sends auth header:
61
+ - `x-release-token: <secret>`
62
+ - Endpoint validates against:
63
+ - `process.env.RELEASE_WEBHOOK_TOKEN`
64
+ - Optional hardening:
65
+ - Add HMAC signature verification for request body.
66
+
67
+ If auth fails:
68
+
69
+ - Return `401` and do not process payload.
70
+
71
+ ## Payload Shape
72
+
73
+ `POST /api/releases` expects JSON:
74
+
75
+ ```json
76
+ {
77
+ "release_id": "build_03252026_1929",
78
+ "version": "1.0.0",
79
+ "published_at": "2026-03-25T19:29:00Z",
80
+ "platform": "windows",
81
+ "assets": [
82
+ {
83
+ "name": "HyperlinksSpaceProgramInstaller.exe",
84
+ "url": "https://github.com/<org>/<repo>/releases/download/build_03252026_1929/HyperlinksSpaceProgramInstaller.exe",
85
+ "sha256": "<optional>"
86
+ }
87
+ ],
88
+ "github_release_url": "https://github.com/<org>/<repo>/releases/tag/build_03252026_1929"
89
+ }
90
+ ```
91
+
92
+ ## Endpoint Behavior (`app/api/releases.ts`)
93
+
94
+ 1. Accept only `POST`.
95
+ 2. Validate auth token/signature.
96
+ 3. Parse and validate required fields:
97
+ - `release_id`, `published_at`, `assets`.
98
+ 4. Enforce idempotency by `release_id`:
99
+ - If already processed, return `200 { ok: true, duplicate: true }`.
100
+ 5. Store/update latest release metadata in persistent storage.
101
+ 6. Push update signal to clients (WebSocket/SSE/Firebase/Expo push).
102
+ 7. Return quickly with `200 { ok: true }`.
103
+
104
+ ## GitHub Actions Notification Step
105
+
106
+ After successful release creation and asset upload:
107
+
108
+ 1. Read webhook URL and token from repository secrets:
109
+ - `RELEASE_WEBHOOK_URL`
110
+ - `RELEASE_WEBHOOK_TOKEN`
111
+ 2. Send `POST` to `/api/releases` with release payload.
112
+ 3. Retry webhook call with backoff on transient failures.
113
+
114
+ Important:
115
+
116
+ - Do not send webhook if release already existed (dedupe branch).
117
+
118
+ ## When You Must Make a New Installer Release
119
+
120
+ Use this section as the decision rule between OTA update and installer release.
121
+
122
+ ### Native/runtime changes (installer required)
123
+
124
+ Create a new installer release when a change affects native binaries or runtime compatibility, including:
125
+
126
+ - Installing, removing, or updating any package that forces you to rebuild the app.
127
+ - Changing app permissions or native capabilities (camera, notifications, background modes, deep links, etc.).
128
+ - Changing package identifiers, signing, entitlements, or other platform build settings.
129
+ - Changing Expo SDK or React Native version in a way that changes native runtime.
130
+ - Changing `runtimeVersion` policy/behavior or bumping app version when runtime compatibility changes.
131
+ - Any change that requires running a fresh native build to take effect.
132
+
133
+ ### Non-native changes (OTA only, no installer)
134
+
135
+ Do not make a new installer release for:
136
+
137
+ - JavaScript/TypeScript business logic changes.
138
+ - UI/layout/style changes.
139
+ - Text/copy changes and static asset updates that are OTA-compatible.
140
+ - Server/API behavior changes that do not require new native modules in app.
141
+
142
+ ### Exact release checklist
143
+
144
+ Make a new installer release if at least one statement is true:
145
+
146
+ 1. "This change cannot work without rebuilding native binaries."
147
+ 2. "This change modifies runtime compatibility between app binary and updates."
148
+ 3. "This change touches native permissions/capabilities/config."
149
+
150
+ If all three are false, ship via OTA update instead of installer release.
151
+
152
+ ### Team policy
153
+
154
+ - Prefer OTA by default for speed.
155
+ - Use installer releases only for native/runtime boundaries.
156
+ - When uncertain, treat as installer-required and verify in staging before production.
157
+
158
+ ## App Update Strategy
159
+
160
+ Primary strategy:
161
+
162
+ - Real-time push signal from backend triggered by `/api/releases`.
163
+
164
+ Fallback strategy:
165
+
166
+ - App checks latest release on:
167
+ - app foreground/resume
168
+ - periodic interval (for example every 5-15 minutes)
169
+
170
+ Client behavior:
171
+
172
+ 1. Compare local build/version with latest server metadata.
173
+ 2. If newer exists:
174
+ - Show "Update available" prompt or trigger controlled update flow.
175
+
176
+ ## Reliability and Observability
177
+
178
+ - Idempotent handling on `release_id`.
179
+ - Structured logs for each stage:
180
+ - detected -> published/skipped -> webhook sent -> app signal broadcast
181
+ - Alert on partial failure:
182
+ - release created but webhook failed
183
+ - Keep webhook processing fast and non-blocking for external calls.
184
+
185
+ ## Rollout Plan
186
+
187
+ 1. Deploy `app/api/releases.ts` in staging.
188
+ 2. Run workflow in dry-run mode to validate folder parsing and dedupe checks.
189
+ 3. Enable real release creation for test folder.
190
+ 4. Enable webhook call to staging endpoint.
191
+ 5. Verify end-to-end with one client device.
192
+ 6. Promote to production after stable validation.
193
+
194
+ ## Acceptance Criteria
195
+
196
+ - A new `app/releases/builder/build_.../` folder produces exactly one GitHub Release.
197
+ - Re-running workflow for the same `release_id` does not create duplicates.
198
+ - Webhook is called only for newly created releases.
199
+ - `POST /api/releases` rejects unauthorized requests.
200
+ - Connected app receives update signal near-instantly in normal conditions.
201
+ - Fallback polling still discovers updates if push delivery fails.
@@ -0,0 +1,188 @@
1
+ # GitHub Releases Automation Plan (Final)
2
+
3
+ This document defines the production plan for release detection, deduped GitHub Release publishing, and near-instant app update signaling.
4
+
5
+ ## Goals
6
+
7
+ - Build the Windows installer on GitHub Actions (`windows-latest`) with `npm run build:win` (no `.exe` committed to git).
8
+ - Publish the installer to a GitHub Release only when that release tag does not already exist.
9
+ - Notify an app-update service immediately after a new release is published.
10
+ - Keep app-side update detection near-instant using push notification plus fallback polling.
11
+
12
+ ## Source of Truth
13
+
14
+ - The **GitHub Release tag** is the release identity (`release_id`), for example `build_03252026_1929`.
15
+ - Locally and in CI, `windows/cleanup.cjs` places the installer under `app/releases/builder/build_MMDDYYYY_HHMM/HyperlinksSpaceProgramInstaller_<stamp>.exe` (other files under `dev/`). In CI, set `RELEASE_BUILD_ID` to pin the folder/tag, or omit it to use the timestamp from build time.
16
+
17
+ ## When You Must Make a New Installer Release
18
+
19
+ Create a new installer/GitHub Release when a change is native/runtime-level and cannot be delivered safely by OTA update alone.
20
+
21
+ Native/runtime changes include:
22
+
23
+ - Adding, removing, or upgrading native libraries/modules (anything requiring a new Android/iOS/desktop binary).
24
+ - Changing app permissions or OS capabilities (camera, notifications, background modes, file access, etc.).
25
+ - Changing Expo config plugins or native project settings that affect the compiled binary.
26
+ - Upgrading Expo SDK/React Native/runtime where binary compatibility changes.
27
+ - Changing `runtimeVersion` strategy/value, or bumping app version when `runtimeVersion.policy` depends on it.
28
+ - Changing signing/notarization/install packaging behavior for distributed installers.
29
+ - Any fix that touches native code or build-time platform configuration.
30
+
31
+ Do not make a new installer release for:
32
+
33
+ - Pure JavaScript/TypeScript logic changes.
34
+ - UI changes in React components.
35
+ - Asset/text/content updates that are OTA-compatible.
36
+ - API integration changes that do not alter native dependencies.
37
+
38
+ Practical decision checklist:
39
+
40
+ 1. Does this change require rebuilding a platform binary to work? If yes, make installer release.
41
+ 2. Does this change alter native modules, permissions, SDK/runtime, or build config? If yes, make installer release.
42
+ 3. If both answers are no, publish OTA update only and skip installer release.
43
+
44
+ ## Workflow Trigger
45
+
46
+ - **Manual only:** `workflow_dispatch` (workflow “Windows release (CI build)”).
47
+ - Optional input **release_id** (`build_MMDDYYYY_HHMM`). If empty, the tag is taken from the build output after `cleanup.cjs`.
48
+
49
+ Example trigger:
50
+
51
+ ```yaml
52
+ on:
53
+ workflow_dispatch:
54
+ inputs:
55
+ release_id:
56
+ description: "Optional tag, e.g. build_03252026_1929"
57
+ required: false
58
+ default: ""
59
+ ```
60
+
61
+ ## Dedupe Rules (No Duplicate Releases)
62
+
63
+ 1. Run `npm run build:win` (optionally with `RELEASE_BUILD_ID` so the output folder matches the intended tag).
64
+ 2. Resolve `release_id` from the workflow input or from `releases/builder/build_*/HyperlinksSpaceProgramInstaller_*.exe`.
65
+ 3. Check whether GitHub Release/tag already exists for that `release_id`.
66
+ 4. If it exists:
67
+ - Exit successfully (`0`)
68
+ - Do not upload assets
69
+ - Do not send webhook notification
70
+ 5. If it does not exist:
71
+ - Create GitHub Release/tag
72
+ - Upload `HyperlinksSpaceProgramInstaller.exe` as the release asset
73
+ - Continue to webhook notification
74
+
75
+ Recommended extra safety:
76
+
77
+ - Use workflow `concurrency` keyed by `release_id` to avoid race conditions.
78
+
79
+ ## Endpoint Contract
80
+
81
+ - Webhook endpoint path: `app/api/releases.ts`
82
+ - Route URL: `POST /api/releases`
83
+ - Purpose: accept release-published events from GitHub Actions and fan out update notifications to app clients.
84
+
85
+ ## Security
86
+
87
+ - GitHub Actions sends auth header:
88
+ - `x-release-token: <secret>`
89
+ - Endpoint validates against:
90
+ - `process.env.RELEASE_WEBHOOK_TOKEN`
91
+ - Optional hardening:
92
+ - Add HMAC signature verification for request body.
93
+
94
+ If auth fails:
95
+
96
+ - Return `401` and do not process payload.
97
+
98
+ ## Payload Shape
99
+
100
+ `POST /api/releases` expects JSON:
101
+
102
+ ```json
103
+ {
104
+ "release_id": "build_03252026_1929",
105
+ "version": "1.0.0",
106
+ "published_at": "2026-03-25T19:29:00Z",
107
+ "platform": "windows",
108
+ "assets": [
109
+ {
110
+ "name": "HyperlinksSpaceProgramInstaller.exe",
111
+ "url": "https://github.com/<org>/<repo>/releases/download/build_03252026_1929/HyperlinksSpaceProgramInstaller.exe",
112
+ "sha256": "<optional>"
113
+ }
114
+ ],
115
+ "github_release_url": "https://github.com/<org>/<repo>/releases/tag/build_03252026_1929"
116
+ }
117
+ ```
118
+
119
+ ## Endpoint Behavior (`app/api/releases.ts`)
120
+
121
+ 1. Accept only `POST`.
122
+ 2. Validate auth token/signature.
123
+ 3. Parse and validate required fields:
124
+ - `release_id`, `published_at`, `assets`.
125
+ 4. Enforce idempotency by `release_id`:
126
+ - If already processed, return `200 { ok: true, duplicate: true }`.
127
+ 5. Store/update latest release metadata in persistent storage.
128
+ 6. Push update signal to clients (WebSocket/SSE/Firebase/Expo push).
129
+ 7. Return quickly with `200 { ok: true }`.
130
+
131
+ ## GitHub Actions Notification Step
132
+
133
+ After successful release creation and asset upload:
134
+
135
+ 1. Read webhook URL and token from repository secrets:
136
+ - `RELEASE_WEBHOOK_URL`
137
+ - `RELEASE_WEBHOOK_TOKEN`
138
+ 2. Send `POST` to `/api/releases` with release payload.
139
+ 3. Retry webhook call with backoff on transient failures.
140
+
141
+ Important:
142
+
143
+ - Do not send webhook if release already existed (dedupe branch).
144
+
145
+ ## App Update Strategy
146
+
147
+ Primary strategy:
148
+
149
+ - Real-time push signal from backend triggered by `/api/releases`.
150
+
151
+ Fallback strategy:
152
+
153
+ - App checks latest release on:
154
+ - app foreground/resume
155
+ - periodic interval (for example every 5-15 minutes)
156
+
157
+ Client behavior:
158
+
159
+ 1. Compare local build/version with latest server metadata.
160
+ 2. If newer exists:
161
+ - Show "Update available" prompt or trigger controlled update flow.
162
+
163
+ ## Reliability and Observability
164
+
165
+ - Idempotent handling on `release_id`.
166
+ - Structured logs for each stage:
167
+ - detected -> published/skipped -> webhook sent -> app signal broadcast
168
+ - Alert on partial failure:
169
+ - release created but webhook failed
170
+ - Keep webhook processing fast and non-blocking for external calls.
171
+
172
+ ## Rollout Plan
173
+
174
+ 1. Deploy `app/api/releases.ts` in staging.
175
+ 2. Run workflow in dry-run mode to validate folder parsing and dedupe checks.
176
+ 3. Enable real release creation for test folder.
177
+ 4. Enable webhook call to staging endpoint.
178
+ 5. Verify end-to-end with one client device.
179
+ 6. Promote to production after stable validation.
180
+
181
+ ## Acceptance Criteria
182
+
183
+ - A new `app/releases/builder/build_.../` folder produces exactly one GitHub Release.
184
+ - Re-running workflow for the same `release_id` does not create duplicates.
185
+ - Webhook is called only for newly created releases.
186
+ - `POST /api/releases` rejects unauthorized requests.
187
+ - Connected app receives update signal near-instantly in normal conditions.
188
+ - Fallback polling still discovers updates if push delivery fails.
@@ -0,0 +1,34 @@
1
+ # Scalability overview and challenges
2
+
3
+ **Stack:** Vercel serverless (webhook, API), Neon Postgres, Telegram bot (Grammy), OpenAI, single `messages` table for bot + TMA.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ - **Webhook:** Stateless. Returns 200 immediately; update is processed in `waitUntil`. No in-memory state between requests. Vercel scales out with traffic (more concurrent requests → more instances).
10
+ - **Bot:** One bot instance per serverless instance (module-level in webhook). No global singleton; fits serverless.
11
+ - **Dedupe / “only latest wins”:** Done in the DB (`telegram_update_id` unique constraint + `getMaxTelegramUpdateIdForThread`). Works across instances; no reliance on in-process state.
12
+ - **Messages:** One table, indexed by `(user_telegram, thread_id, type, created_at)` and unique on `(..., telegram_update_id)` for bot. Per-request load is small (insert user, get history, insert assistant, optional max-update-id read).
13
+ - **Database:** Neon with **connection pooling** already on (supports up to 10,000 concurrent connections). Use the pooled `DATABASE_URL` in Vercel so serverless invocations go through the pooler.
14
+
15
+ The design is horizontally scalable: more users → more invocations → more instances; no single bottleneck in the app logic.
16
+
17
+ ---
18
+
19
+ ## Challenges and what to do
20
+
21
+ | Area | Challenge | What to do |
22
+ |------|-----------|------------|
23
+ | **DB connections** | Pooling is already on (up to 10,000 concurrent connections). | Ensure `DATABASE_URL` in Vercel is the **pooled** connection string from Neon. |
24
+ | **OpenAI** | Limits are per key (RPM/TPM). One key can scale. | Raise **usage tier** (limits increase with spend/account age—e.g. Tier 4: 10k RPM, 2M TPM). Enterprise: **Scale Tier** for dedicated capacity. Optionally multiple keys or queue only if you need beyond tier limits. |
25
+ | **Vercel** | Hobby has invocation/duration limits; long AI flows need enough timeout. | Use Pro (or Enterprise) for production at scale; set function timeout to cover longest flow (e.g. streaming + DB). |
26
+ | **Messages table** | Grows with users and time; very large tables can slow queries and increase cost. | Retention (delete or archive old threads); optional partitioning by time or user; keep `getThreadHistory` with a small `limit`. |
27
+ | **Observability** | At scale, bottlenecks and errors are hard to see without metrics. | Logging, metrics, and alerts (errors, latency, DB pool usage, OpenAI rate limits) in Vercel/Neon or a third-party tool. |
28
+
29
+ ---
30
+
31
+ ## Summary
32
+
33
+ - **Scalable:** Stateless webhook, 200-first response, DB-backed dedupe and history, Neon connection pooling.
34
+ - **To scale to very high traffic:** Neon pooling is already on (10k connections); keep using the pooled `DATABASE_URL`. For OpenAI, extend plan/usage to reach a higher tier (or Scale Tier for enterprise); one key is enough. Plan for Vercel limits/timeouts; add message retention and monitoring.