@www.hyperlinks.space/program-kit 1.2.181818 → 7.8.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.
- package/.eas/workflows/create-development-builds.yml +21 -0
- package/.eas/workflows/create-draft.yml +15 -0
- package/.eas/workflows/deploy-to-production.yml +68 -0
- package/.env.example +19 -0
- package/.gitattributes +48 -0
- package/.gitignore +52 -0
- package/.nvmrc +1 -0
- package/.vercelignore +6 -0
- package/README.md +7 -2
- package/ai/openai.ts +202 -0
- package/ai/transmitter.ts +367 -0
- package/api/{base.ts → _base.ts} +1 -1
- package/api/wallet/_auth.ts +143 -0
- package/api/wallet/register.ts +151 -0
- package/api/wallet/status.ts +89 -0
- package/app/index.tsx +319 -5
- package/assets/images/PreviewImage.png +0 -0
- package/backlogs/medium_term_backlog.md +26 -0
- package/backlogs/short_term_backlog.md +42 -0
- package/database/start.ts +0 -1
- package/database/wallets.ts +266 -0
- package/eslint.config.cjs +10 -0
- package/fullREADME.md +142 -71
- package/index.js +3 -0
- package/npmReadMe.md +7 -2
- package/npmrc.example +1 -0
- package/package.json +7 -27
- package/polyfills/buffer.ts +9 -0
- package/research & docs/auth-and-centralized-encrypted-keys-plan.md +440 -0
- package/research & docs/github-gitlab-bidirectional-mirroring.md +154 -0
- package/research & docs/keys-retrieval-console-scripts.js +131 -0
- package/{docs/security_plan_raw.md → research & docs/security_plan_raw.md } +1 -1
- package/{docs/security_raw.md → research & docs/security_raw.md } +22 -13
- package/research & docs/storage-availability-console-script.js +152 -0
- package/research & docs/storage-lifetime.md +33 -0
- package/research & docs/telegram-raw-keys-cloud-storage-risks.md +31 -0
- package/{docs/wallets_hosting_architecture.md → research & docs/wallets_hosting_architecture.md } +147 -1
- package/scripts/test-api-base.ts +2 -2
- package/services/wallet/tonWallet.ts +73 -0
- package/telegram/post.ts +44 -8
- package/ui/components/GlobalBottomBar.tsx +447 -0
- package/ui/components/GlobalBottomBarWeb.tsx +362 -0
- package/ui/components/GlobalLogoBar.tsx +108 -0
- package/ui/components/GlobalLogoBarFallback.tsx +66 -0
- package/ui/components/GlobalLogoBarWithFallback.tsx +24 -0
- package/ui/components/HyperlinksSpaceLogo.tsx +29 -0
- package/ui/components/Telegram.tsx +677 -0
- package/ui/components/telegramWebApp.ts +359 -0
- package/ui/fonts.ts +12 -0
- package/ui/theme.ts +117 -0
- /package/{docs → research & docs}/ai_and_search_bar_input.md +0 -0
- /package/{docs → research & docs}/ai_bot_messages.md +0 -0
- /package/{docs → research & docs}/blue_bar_tackling.md +0 -0
- /package/{docs → research & docs}/bot_async_streaming.md +0 -0
- /package/{docs → research & docs}/build_and_install.md +0 -0
- /package/{docs → research & docs}/database_messages.md +0 -0
- /package/{docs → research & docs}/fonts.md +0 -0
- /package/{docs → research & docs}/npm-release.md +0 -0
- /package/{docs → research & docs}/releases.md +0 -0
- /package/{docs → research & docs}/releases_github_actions.md +0 -0
- /package/{docs → research & docs}/scalability.md +0 -0
- /package/{docs → research & docs}/timing_raw.md +0 -0
- /package/{docs → research & docs}/tma_logo_bar_jump_investigation.md +0 -0
- /package/{docs → research & docs}/update.md +0 -0
- /package/{docs → research & docs}/wallet_telegram_standalone_multichain_proposal.md +0 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
# Auth + Centralized Encrypted Keys Plan (Supabase)
|
|
2
|
+
|
|
3
|
+
This document defines a practical implementation plan for:
|
|
4
|
+
|
|
5
|
+
- Multi-provider login: **Google**, **Telegram**, **GitHub**, and **email + protection code (OTP)**
|
|
6
|
+
- Centralized storage in **Supabase**
|
|
7
|
+
- Wallet secrets stored as **encrypted blobs only** (no plaintext mnemonic/private keys server-side)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1) Product Goal
|
|
12
|
+
|
|
13
|
+
Enable users to sign in from multiple platforms and recover wallet access from Supabase by using account credentials plus a user-held decryption secret model.
|
|
14
|
+
|
|
15
|
+
## 2) Security Goal
|
|
16
|
+
|
|
17
|
+
- Supabase/backend stores only ciphertext envelopes and metadata.
|
|
18
|
+
- Decryption happens client-side.
|
|
19
|
+
- Server does not receive plaintext mnemonic/private keys.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 3) Trust Model Decision
|
|
24
|
+
|
|
25
|
+
To avoid custodial key handling by backend, choose one of these decryption models:
|
|
26
|
+
|
|
27
|
+
1. **Password-derived key model (recommended for centralized sync)**
|
|
28
|
+
- User sets a wallet passphrase.
|
|
29
|
+
- Client derives key with Argon2id.
|
|
30
|
+
- Ciphertext stored in Supabase.
|
|
31
|
+
2. **Device-key model only**
|
|
32
|
+
- Better local UX, weaker cross-device recovery unless mnemonic re-entry is required.
|
|
33
|
+
|
|
34
|
+
For this plan, use model (1) as canonical centralized recovery path.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 3.1) What "wrapped decrypt key" means (plain language)
|
|
39
|
+
|
|
40
|
+
This phrase means we do not store the real decryption key directly in the database.
|
|
41
|
+
|
|
42
|
+
- **DEK (Data Encryption Key):** key that encrypts wallet secret/mnemonic.
|
|
43
|
+
- **KEK (Key Encryption Key):** key that encrypts ("wraps") the DEK.
|
|
44
|
+
|
|
45
|
+
So database stores:
|
|
46
|
+
- wallet ciphertext (encrypted by DEK)
|
|
47
|
+
- wrapped DEK (encrypted by KEK)
|
|
48
|
+
|
|
49
|
+
Database does **not** store:
|
|
50
|
+
- plaintext mnemonic
|
|
51
|
+
- plaintext DEK
|
|
52
|
+
- plaintext KEK
|
|
53
|
+
|
|
54
|
+
### Why KMS/HSM is mentioned
|
|
55
|
+
|
|
56
|
+
`KEK` should live in a managed key system (AWS KMS, GCP KMS, Azure Key Vault HSM, etc.), not in app code or DB columns.
|
|
57
|
+
|
|
58
|
+
When app needs decrypt flow:
|
|
59
|
+
1. User authenticates (Google/Telegram/GitHub/email OTP).
|
|
60
|
+
2. Backend loads `wrapped_dek` + wallet `ciphertext` from DB.
|
|
61
|
+
3. Backend asks KMS/HSM to unwrap DEK (or returns a short-lived tokenized decrypt result, depending on policy).
|
|
62
|
+
4. Decrypt happens in the chosen boundary (client-side or controlled backend flow).
|
|
63
|
+
|
|
64
|
+
### Tiny analogy
|
|
65
|
+
|
|
66
|
+
- Wallet data = document in locked box (ciphertext).
|
|
67
|
+
- DEK = key to that box.
|
|
68
|
+
- KEK = key to a safe that contains the DEK.
|
|
69
|
+
- KMS/HSM = guarded safe room.
|
|
70
|
+
|
|
71
|
+
This way, stealing only DB rows is not enough to decrypt user wallets.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3.2) Newbie note: "But ciphertext and wrapped key are in the same DB"
|
|
76
|
+
|
|
77
|
+
This is a common concern and the short answer is:
|
|
78
|
+
|
|
79
|
+
- Yes, they can be stored in the same row.
|
|
80
|
+
- No, that does not automatically break security.
|
|
81
|
+
|
|
82
|
+
### Why this can still be safe
|
|
83
|
+
|
|
84
|
+
Think of three pieces:
|
|
85
|
+
|
|
86
|
+
1. `ciphertext` (locked wallet data)
|
|
87
|
+
2. `wrapped_dek` (the key to the lock, but itself locked)
|
|
88
|
+
3. `kek` in KMS/HSM (the key that unlocks `wrapped_dek`)
|
|
89
|
+
|
|
90
|
+
If attacker steals DB only, they get (1) and (2), but not (3).
|
|
91
|
+
Without (3), they cannot recover plaintext keys.
|
|
92
|
+
|
|
93
|
+
### What actually protects the system
|
|
94
|
+
|
|
95
|
+
The real protection is **access control to KMS/HSM**, not hiding DB relations.
|
|
96
|
+
|
|
97
|
+
- Keep KEK outside DB.
|
|
98
|
+
- Restrict KMS permissions to minimum required service path.
|
|
99
|
+
- Audit and rate-limit unwrap operations.
|
|
100
|
+
|
|
101
|
+
### Optional extra hardening
|
|
102
|
+
|
|
103
|
+
You can split storage across two databases (ciphertext in one, wrapped key in another), but this is additional defense-in-depth. It does not replace KMS/HSM boundary.
|
|
104
|
+
|
|
105
|
+
Rule of thumb for beginners:
|
|
106
|
+
**Do not rely on "they cannot match rows"; rely on "they cannot access KEK".**
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 3.3) How user session works with KEK (Google/Telegram/GitHub/email OTP)
|
|
111
|
+
|
|
112
|
+
Important: user login session is an **authorization signal**, not the KEK itself.
|
|
113
|
+
|
|
114
|
+
- Google/Telegram/GitHub/email OTP proves "this user is authenticated".
|
|
115
|
+
- Backend then decides whether to allow key unwrap path.
|
|
116
|
+
- KEK remains in KMS/HSM and is never replaced by OAuth/session token.
|
|
117
|
+
|
|
118
|
+
Typical flow:
|
|
119
|
+
|
|
120
|
+
1. User logs in and gets a valid app session.
|
|
121
|
+
2. App requests wallet unlock/decrypt operation.
|
|
122
|
+
3. Backend verifies session + policy checks (device/risk/rate limits).
|
|
123
|
+
4. Backend reads `wrapped_dek + ciphertext` from DB.
|
|
124
|
+
5. Backend calls KMS/HSM to unwrap DEK.
|
|
125
|
+
6. Decrypt/sign path continues under selected trust boundary.
|
|
126
|
+
|
|
127
|
+
This is why people say "session gates KEK usage", not "session is KEK".
|
|
128
|
+
|
|
129
|
+
### Two deployment variants
|
|
130
|
+
|
|
131
|
+
- **Variant A (more custodial):**
|
|
132
|
+
- Backend unwraps DEK and performs decrypt/sign server-side.
|
|
133
|
+
- User gets signed result/tx hash.
|
|
134
|
+
- **Variant B (hybrid):**
|
|
135
|
+
- Backend authorizes and returns short-lived decrypt material/session token.
|
|
136
|
+
- Client resolves/decrypts locally for signing.
|
|
137
|
+
|
|
138
|
+
Choose Variant A only if you explicitly accept custodial responsibility.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## 3.4) Why keep both `wrapped_dek` and `ciphertext` in DB (not one encrypted entity)
|
|
143
|
+
|
|
144
|
+
You can think "why not one giant blob encrypted by KEK directly?".
|
|
145
|
+
Short answer: envelope encryption with separate DEK is safer and more operationally practical.
|
|
146
|
+
|
|
147
|
+
Reasons:
|
|
148
|
+
|
|
149
|
+
1. **KMS/HSM usage limits and performance**
|
|
150
|
+
- KMS is best for wrapping small keys, not encrypting large/high-volume payloads repeatedly.
|
|
151
|
+
- DEK handles data encryption efficiently.
|
|
152
|
+
|
|
153
|
+
2. **Key rotation without re-encrypting all wallet data**
|
|
154
|
+
- Rotate KEK by re-wrapping DEKs.
|
|
155
|
+
- No need to decrypt/re-encrypt every wallet ciphertext each time KEK rotates.
|
|
156
|
+
|
|
157
|
+
3. **Cryptographic separation of duties**
|
|
158
|
+
- DEK protects wallet payload.
|
|
159
|
+
- KEK protects DEK.
|
|
160
|
+
- Cleaner blast-radius control and auditing.
|
|
161
|
+
|
|
162
|
+
4. **Metadata and versioning flexibility**
|
|
163
|
+
- You can evolve ciphertext formats (AEAD params/version) independently from KEK lifecycle.
|
|
164
|
+
|
|
165
|
+
5. **Standard industry pattern**
|
|
166
|
+
- This is standard "envelope encryption" used by major cloud security systems.
|
|
167
|
+
|
|
168
|
+
So storing both `ciphertext` and `wrapped_dek` is expected architecture, not duplication.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 3.5) KMS/HSM section: what it is and how to operate it
|
|
173
|
+
|
|
174
|
+
## What is KMS?
|
|
175
|
+
|
|
176
|
+
**KMS (Key Management Service)** is a managed service that stores and uses cryptographic master keys with strict access controls, logging, and rotation features.
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
- AWS KMS
|
|
180
|
+
- Google Cloud KMS
|
|
181
|
+
- Azure Key Vault (with HSM-backed options)
|
|
182
|
+
|
|
183
|
+
## What is HSM?
|
|
184
|
+
|
|
185
|
+
**HSM (Hardware Security Module)** is specialized hardware designed to keep key material protected and perform crypto operations with strong tamper resistance.
|
|
186
|
+
|
|
187
|
+
In practice:
|
|
188
|
+
- Many cloud KMS offerings can use HSM-backed keys.
|
|
189
|
+
- Teams usually start with managed KMS and move to stricter HSM policies if required by risk/compliance.
|
|
190
|
+
|
|
191
|
+
## Why use KMS/HSM here
|
|
192
|
+
|
|
193
|
+
- Keep `KEK` out of application DB and source code.
|
|
194
|
+
- Centralize key policy and access control.
|
|
195
|
+
- Get immutable audit logs for unwrap/encrypt/decrypt operations.
|
|
196
|
+
- Support controlled key rotation and key disable/emergency revoke.
|
|
197
|
+
|
|
198
|
+
## How to deal with it (practical checklist)
|
|
199
|
+
|
|
200
|
+
1. **Create KEK in KMS/HSM**
|
|
201
|
+
- Mark as non-exportable when available.
|
|
202
|
+
- Separate key per environment (`dev/stage/prod`).
|
|
203
|
+
|
|
204
|
+
2. **Restrict IAM permissions**
|
|
205
|
+
- App service can only call required operations (typically `Decrypt/Unwrap`, maybe `Encrypt/Wrap`).
|
|
206
|
+
- No broad admin access from runtime services.
|
|
207
|
+
|
|
208
|
+
3. **Use envelope encryption pattern**
|
|
209
|
+
- Generate DEK for wallet payload encryption.
|
|
210
|
+
- Store `ciphertext + wrapped_dek` in DB.
|
|
211
|
+
- Never store plaintext KEK/DEK at rest.
|
|
212
|
+
|
|
213
|
+
4. **Add policy checks before unwrap**
|
|
214
|
+
- Require valid session and account state.
|
|
215
|
+
- Apply risk checks (IP/device anomalies, rate limits, cooldowns).
|
|
216
|
+
- Log every sensitive operation.
|
|
217
|
+
|
|
218
|
+
5. **Implement rotation**
|
|
219
|
+
- Rotate KEK on schedule.
|
|
220
|
+
- Re-wrap DEKs in background jobs.
|
|
221
|
+
- Keep key version metadata in DB.
|
|
222
|
+
|
|
223
|
+
6. **Prepare incident controls**
|
|
224
|
+
- Ability to disable key version quickly.
|
|
225
|
+
- Emergency freeze for high-risk accounts.
|
|
226
|
+
- Recovery runbook for key compromise scenarios.
|
|
227
|
+
|
|
228
|
+
## Common mistakes to avoid
|
|
229
|
+
|
|
230
|
+
- Putting KEK plaintext in `.env` and calling it "KMS-ready".
|
|
231
|
+
- Giving app runtime full KMS admin permissions.
|
|
232
|
+
- Missing unwrap rate limits and anomaly detection.
|
|
233
|
+
- No audit review pipeline for key operations.
|
|
234
|
+
- Designing without key-rotation path from day one.
|
|
235
|
+
|
|
236
|
+
## Newbie rule
|
|
237
|
+
|
|
238
|
+
If DB is stolen, attacker should still need **separate KMS/HSM access** to decrypt anything.
|
|
239
|
+
If DB theft alone can decrypt wallets, architecture is wrong.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 4) Authentication Architecture
|
|
244
|
+
|
|
245
|
+
Use **Supabase Auth** as identity layer:
|
|
246
|
+
|
|
247
|
+
- Google OAuth
|
|
248
|
+
- GitHub OAuth
|
|
249
|
+
- Email + protection code (OTP / magic code)
|
|
250
|
+
- Telegram login bridge (custom verifier service; link to Supabase user)
|
|
251
|
+
|
|
252
|
+
### Identity Linking
|
|
253
|
+
|
|
254
|
+
A single user can link multiple auth methods to one account.
|
|
255
|
+
|
|
256
|
+
- Primary identity key: `user_id` (Supabase Auth UUID)
|
|
257
|
+
- Linked providers table stores provider identifiers (`google_sub`, `github_id`, `telegram_id`, etc.)
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 5) Wallet Encryption Envelope
|
|
262
|
+
|
|
263
|
+
Store one or more encrypted wallet envelopes per user.
|
|
264
|
+
|
|
265
|
+
### Envelope format (server-stored)
|
|
266
|
+
|
|
267
|
+
- `ciphertext` (base64)
|
|
268
|
+
- `nonce/iv`
|
|
269
|
+
- `kdf` params:
|
|
270
|
+
- `algorithm = argon2id`
|
|
271
|
+
- `salt`
|
|
272
|
+
- `memory_kib`
|
|
273
|
+
- `iterations`
|
|
274
|
+
- `parallelism`
|
|
275
|
+
- `dk_len`
|
|
276
|
+
- `aead`:
|
|
277
|
+
- `algorithm = aes-256-gcm` (or xchacha20-poly1305)
|
|
278
|
+
- optional `aad`
|
|
279
|
+
- `version`
|
|
280
|
+
- `created_at`, `updated_at`
|
|
281
|
+
|
|
282
|
+
### Crypto rules
|
|
283
|
+
|
|
284
|
+
- KDF and encryption run **client-side only**.
|
|
285
|
+
- Never send passphrase to server.
|
|
286
|
+
- Never log secrets/ciphertext in verbose logs.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 6) Supabase Data Model
|
|
291
|
+
|
|
292
|
+
## `profiles`
|
|
293
|
+
- `id` (uuid, fk -> auth.users.id)
|
|
294
|
+
- `username` (nullable unique)
|
|
295
|
+
- `display_name`
|
|
296
|
+
- `created_at`
|
|
297
|
+
|
|
298
|
+
## `auth_identities`
|
|
299
|
+
- `id` (uuid)
|
|
300
|
+
- `user_id` (uuid)
|
|
301
|
+
- `provider` (`google|github|telegram|email_otp`)
|
|
302
|
+
- `provider_subject` (string)
|
|
303
|
+
- unique (`provider`, `provider_subject`)
|
|
304
|
+
|
|
305
|
+
## `wallet_envelopes`
|
|
306
|
+
- `id` (uuid)
|
|
307
|
+
- `user_id` (uuid)
|
|
308
|
+
- `wallet_label`
|
|
309
|
+
- `ciphertext`
|
|
310
|
+
- `kdf_json`
|
|
311
|
+
- `aead_json`
|
|
312
|
+
- `envelope_version`
|
|
313
|
+
- `is_active`
|
|
314
|
+
- timestamps
|
|
315
|
+
|
|
316
|
+
## `security_events`
|
|
317
|
+
- `id`, `user_id`
|
|
318
|
+
- `event_type`
|
|
319
|
+
- `ip_hash`, `ua_hash`
|
|
320
|
+
- timestamps
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## 7) Authorization and RLS
|
|
325
|
+
|
|
326
|
+
Enable Row Level Security:
|
|
327
|
+
|
|
328
|
+
- user can read/write only rows where `user_id = auth.uid()`.
|
|
329
|
+
- admin/service role separated for backend-only tasks.
|
|
330
|
+
- strict policies for envelope update/delete.
|
|
331
|
+
|
|
332
|
+
Add rate limits and abuse controls at API edge:
|
|
333
|
+
|
|
334
|
+
- login attempt throttling
|
|
335
|
+
- envelope fetch/update throttling
|
|
336
|
+
- device/IP anomaly checks
|
|
337
|
+
|
|
338
|
+
---
|
|
339
|
+
|
|
340
|
+
## 8) Provider-Specific Notes
|
|
341
|
+
|
|
342
|
+
## Google / GitHub
|
|
343
|
+
- Use Supabase OAuth providers.
|
|
344
|
+
- Standard callback + session issuance.
|
|
345
|
+
|
|
346
|
+
## Email + Protection Code (OTP)
|
|
347
|
+
- Use Supabase OTP flow (`signInWithOtp`) with short code expiry.
|
|
348
|
+
- Rate-limit OTP requests and verify attempts.
|
|
349
|
+
- Add anti-abuse checks (per-IP/per-email cooldown).
|
|
350
|
+
- Optional: require email verification before provider linking actions.
|
|
351
|
+
|
|
352
|
+
## Telegram
|
|
353
|
+
- Verify Telegram login payload (`id_token`/signed data) in backend function.
|
|
354
|
+
- On success, link Telegram identity to existing `user_id` or create new profile.
|
|
355
|
+
- Telegram auth is identity only, not decryption secret.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 9) Client Flows
|
|
360
|
+
|
|
361
|
+
## Registration
|
|
362
|
+
1. User signs up via any provider.
|
|
363
|
+
2. App prompts wallet passphrase setup (if no envelope exists).
|
|
364
|
+
3. Client generates/imports mnemonic.
|
|
365
|
+
4. Client encrypts mnemonic -> envelope.
|
|
366
|
+
5. Upload envelope to Supabase.
|
|
367
|
+
|
|
368
|
+
## Login on new device
|
|
369
|
+
1. User authenticates via provider.
|
|
370
|
+
2. App fetches envelope from Supabase.
|
|
371
|
+
3. User enters wallet passphrase.
|
|
372
|
+
4. Client decrypts locally and unlocks wallet.
|
|
373
|
+
|
|
374
|
+
## Passphrase change
|
|
375
|
+
1. Unlock with current passphrase.
|
|
376
|
+
2. Re-encrypt with new Argon2id salt/params.
|
|
377
|
+
3. Replace envelope atomically.
|
|
378
|
+
|
|
379
|
+
## Email protection-code recovery
|
|
380
|
+
1. User requests login code by email.
|
|
381
|
+
2. Enters code and gets authenticated session.
|
|
382
|
+
3. If wallet envelope exists, user enters wallet passphrase to decrypt.
|
|
383
|
+
4. If passphrase is forgotten, user must recover with mnemonic and set a new passphrase.
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 10) Operational Security Requirements
|
|
388
|
+
|
|
389
|
+
- CSP hardening and dependency pinning for web/TMA.
|
|
390
|
+
- Secrets scanning and signed CI artifacts.
|
|
391
|
+
- Audit trail for sensitive actions.
|
|
392
|
+
- Incident playbook for account takeover and suspicious activity.
|
|
393
|
+
- User alerts (new login, passphrase changed, provider linked/unlinked).
|
|
394
|
+
- User alerts (new OTP login, passphrase changed, provider linked/unlinked).
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
## 11) Migration Plan (from current model)
|
|
399
|
+
|
|
400
|
+
1. Keep existing TMA SecureStorage/DeviceStorage path active.
|
|
401
|
+
2. Add optional centralized envelope creation for users.
|
|
402
|
+
3. Backfill on next successful unlock/sign event.
|
|
403
|
+
4. After adoption, use centralized envelope as cross-device recovery path.
|
|
404
|
+
5. Maintain mnemonic-first emergency recovery.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 12) Scope, Non-goals, and Warnings
|
|
409
|
+
|
|
410
|
+
Non-goals:
|
|
411
|
+
- Backend plaintext key custody
|
|
412
|
+
- Signing in backend with user mnemonic/private key
|
|
413
|
+
|
|
414
|
+
Warnings:
|
|
415
|
+
- If user forgets both mnemonic and passphrase, recovery is impossible by design.
|
|
416
|
+
- Centralized ciphertext still increases account-takeover pressure; defense must focus on OTP hardening, rate limits, and alerts.
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## 13) Phase Breakdown
|
|
421
|
+
|
|
422
|
+
## Phase A: Identity foundation
|
|
423
|
+
- Configure Supabase Auth providers.
|
|
424
|
+
- Add account linking UX.
|
|
425
|
+
- Add RLS and identity tables.
|
|
426
|
+
|
|
427
|
+
## Phase B: Envelope crypto
|
|
428
|
+
- Implement client Argon2id + AEAD module.
|
|
429
|
+
- Add `wallet_envelopes` APIs and tests.
|
|
430
|
+
|
|
431
|
+
## Phase C: Recovery UX
|
|
432
|
+
- New device unlock flow.
|
|
433
|
+
- Passphrase reset/re-encrypt flow.
|
|
434
|
+
|
|
435
|
+
## Phase D: Hardening
|
|
436
|
+
- Abuse controls, telemetry, alerts, and audits.
|
|
437
|
+
|
|
438
|
+
## Phase E: Rollout
|
|
439
|
+
- Feature flags, gradual rollout, and migration metrics.
|
|
440
|
+
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# GitHub and GitLab Repository Mirroring
|
|
2
|
+
|
|
3
|
+
This document describes how to keep a Git repository synchronized between GitHub and GitLab, including **bidirectional** setups, the problems that appear in practice, **automation guards** that reduce loops and wasted work, and **automatic escalation** when the automation must not guess (for example, divergent histories).
|
|
4
|
+
|
|
5
|
+
It is guidance for operators and automation authors; it is not specific to application code in this repository.
|
|
6
|
+
|
|
7
|
+
## Terminology
|
|
8
|
+
|
|
9
|
+
- **Canonical remote:** The single place where humans are expected to push day-to-day changes (or where merge commits land after review). Everything else is a **mirror**.
|
|
10
|
+
- **One-way mirror:** After a push to A, automation updates B. Humans do not push to B for the same branches (or B is protected so only the mirror bot can push).
|
|
11
|
+
- **Bidirectional mirror:** Automation tries to propagate pushes from A→B and B→A. This is workable only with strict guards and a clear policy when histories **diverge**.
|
|
12
|
+
- **Escalate:** Stop silent auto-fix; surface the condition to people or ticketing (failed job, alert, issue). See [Automatic escalation](#automatic-escalation).
|
|
13
|
+
|
|
14
|
+
## Recommended default: one canonical remote + one-way mirror
|
|
15
|
+
|
|
16
|
+
For most teams, the lowest-risk approach is:
|
|
17
|
+
|
|
18
|
+
1. Choose **one** platform as the source of truth for each protected branch (usually `main`).
|
|
19
|
+
2. Mirror to the other platform with **fast-forward-only** pushes and **short-circuit** when tips already match.
|
|
20
|
+
|
|
21
|
+
Typical implementations (pick **one** row: canonical host is where humans push; the arrow is where commits are **copied** next):
|
|
22
|
+
|
|
23
|
+
| Canonical | Replication direction | How |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| **GitHub** | GitHub → GitLab | GitHub Actions on `push`: fetch GitLab, compare SHAs, `git push` to GitLab using a [GitLab deploy key](https://docs.gitlab.com/ee/user/project/deploy_keys/) or [project access token](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html). |
|
|
26
|
+
| **GitLab** | GitHub ← GitLab | [GitLab repository push mirroring](https://docs.gitlab.com/ee/user/project/repository/mirror/) so GitLab pushes to GitHub after receiving commits. |
|
|
27
|
+
|
|
28
|
+
The two directions are **not** used together for the same branch in this pattern: they are **alternatives** depending on which platform is source of truth. (Using both at once without guards is how bidirectional mirroring starts.)
|
|
29
|
+
|
|
30
|
+
Official overview: [GitLab repository mirroring](https://docs.gitlab.com/ee/user/project/repository/mirror/), [GitHub Actions push trigger](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push).
|
|
31
|
+
|
|
32
|
+
This gives **near-instant** propagation from the moment the canonical host accepts the push, without fighting bidirectional edge cases.
|
|
33
|
+
|
|
34
|
+
## Bidirectional mirroring: when it is used and what breaks
|
|
35
|
+
|
|
36
|
+
Bidirectional setups appear when both platforms must accept pushes (different teams, migration, or CI only on one side). They are **not** “set and forget.”
|
|
37
|
+
|
|
38
|
+
| Risk | What goes wrong | Why it matters |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| **Loop / ping-pong** | A push to GitHub triggers sync to GitLab; GitLab triggers sync back to GitHub; repeat or duplicate work | Duplicate CI, wasted API calls, flaky pipelines |
|
|
41
|
+
| **New objects from automation** | Mirror job merges, rebases, or force-pushes to “fix” state | New commits or rewritten history; harder audits; can feed loops |
|
|
42
|
+
| **Divergence** | Two different commits on `main` at each host, neither is ancestor of the other | `git push` rejects (non-fast-forward) unless someone force-pushes |
|
|
43
|
+
| **Duplicate CI** | Same tree built twice because both hosts run pipelines on the same logical change | Cost and noise; confusing status checks |
|
|
44
|
+
| **Credential exposure** | Tokens in logs, overly broad PATs, shared user accounts | Security and compliance issues |
|
|
45
|
+
|
|
46
|
+
## Guards in automation (layered)
|
|
47
|
+
|
|
48
|
+
Use several of these together; one guard rarely covers every failure mode.
|
|
49
|
+
|
|
50
|
+
### 1. Event and scheduling guards
|
|
51
|
+
|
|
52
|
+
- **SHA equality short-circuit:** After fetching both remotes, if `github/main` and `gitlab/main` are the **same commit OID**, exit successfully without pushing.
|
|
53
|
+
- **Ancestor / fast-forward check before push:** Only push from A to B if B’s tip is an **ancestor** of A’s tip (pure fast-forward). If not, do not force-push; treat as divergence and [escalate](#automatic-escalation).
|
|
54
|
+
- **Mirror bot identity:** Perform mirror pushes with a **dedicated** bot user or deploy key. On the receiving side, webhook or pipeline logic can **skip enqueuing reverse sync** when the actor matches the mirror bot (where the platform exposes this).
|
|
55
|
+
- **Idempotency:** Persist “last mirrored OID per branch” (CI cache, KV, issue comment, etc.) and skip if the incoming event’s `after` SHA was already mirrored.
|
|
56
|
+
- **Debounce / concurrency:** One in-flight sync per branch (GitHub Actions `concurrency`, GitLab `resource_group`, or a lock) to avoid stacked identical jobs from webhook retries.
|
|
57
|
+
|
|
58
|
+
### 2. Git protocol guards
|
|
59
|
+
|
|
60
|
+
- **No `--force` on mirrored branches** unless you have an explicit, rare break-glass procedure. Non-fast-forward should **fail** and escalate.
|
|
61
|
+
- **Narrow refspecs:** Mirror only agreed branches (for example `refs/heads/main`, `refs/heads/release/*`), not all refs.
|
|
62
|
+
- **Atomic multi-ref push** when supported (`git push --atomic`) to avoid half-updated mirror state.
|
|
63
|
+
|
|
64
|
+
### 3. Loop-specific logic
|
|
65
|
+
|
|
66
|
+
- **Directional rule:** GitHub→GitLab runs only when GitHub is strictly ahead of GitLab (fast-forward). GitLab→GitHub runs only when GitLab is strictly ahead. If **both** are ahead of the common ancestor (diverged), **neither** direction should force-sync; escalate.
|
|
67
|
+
- **Cooldown:** If the same `(branch, OID)` was mirrored within N seconds, exit (reduces double webhook storms).
|
|
68
|
+
|
|
69
|
+
### 4. CI noise controls
|
|
70
|
+
|
|
71
|
+
Mirroring duplicates pipelines if both hosts run on every push.
|
|
72
|
+
|
|
73
|
+
- Prefer **one** platform for **required** checks; treat the other as informational or limit runs (for example scheduled smoke only).
|
|
74
|
+
- Where policy allows, use host-specific **skip directives** for mirror-only pushes (conventions vary; confirm org rules).
|
|
75
|
+
- **Path filters** help only when mirror commits change nothing meaningful; usually both sides see the same files.
|
|
76
|
+
|
|
77
|
+
### 5. Security guards
|
|
78
|
+
|
|
79
|
+
- **Least privilege:** Repo-scoped tokens or deploy keys; no org-wide admin tokens for mirroring.
|
|
80
|
+
- **Branch protection** on both sides: block force-push to mirrored branches; restrict who may push to protected branches.
|
|
81
|
+
- **Secrets hygiene:** Mask remotes in logs; disable interactive prompts (`GIT_TERMINAL_PROMPT=0`); store credentials only in CI secrets.
|
|
82
|
+
|
|
83
|
+
## Automatic escalation
|
|
84
|
+
|
|
85
|
+
**Escalate** means: the automation **detects a condition it must not resolve alone**, marks the run as failed or blocked, and **notifies** or **files a ticket** with enough context for a human to decide.
|
|
86
|
+
|
|
87
|
+
This is the opposite of silently force-pushing or auto-merging without policy.
|
|
88
|
+
|
|
89
|
+
### When to escalate automatically
|
|
90
|
+
|
|
91
|
+
Typical conditions:
|
|
92
|
+
|
|
93
|
+
- **Divergence:** `main` on GitHub and GitLab point to different commits and neither is an ancestor of the other (merge-base exists but both tips have unique commits).
|
|
94
|
+
- **Non-fast-forward push rejected:** Mirror push fails with non-FF; do not retry with force.
|
|
95
|
+
- **Repeated failures:** Same branch fails N times in a row (may indicate permission, quota, or hook problems).
|
|
96
|
+
- **Unexpected ref state:** Missing branch, empty repo, or OID mismatch after push (verify step fails).
|
|
97
|
+
|
|
98
|
+
### What “automatic escalation” can do (concrete patterns)
|
|
99
|
+
|
|
100
|
+
Implement one or more of the following from the same job that detected the problem:
|
|
101
|
+
|
|
102
|
+
1. **Fail the pipeline** with a clear title and non-zero exit code so the default branch protection and dashboards show breakage.
|
|
103
|
+
2. **Post a structured comment** on a pinned tracking issue (GitHub Issues / GitLab issue) including: branch name, both OIDs, link to the failed pipeline, and a one-line `git` hint (`git merge-base`, `git log --left-right`) for the on-call engineer.
|
|
104
|
+
3. **Send chat or email** via webhook (Slack incoming webhook, Microsoft Teams, email SMTP API) with the same summary fields.
|
|
105
|
+
4. **Open a new issue** (if API token allows) with label `mirror-divergence` so work is tracked and deduplicated (search for open issues with the same branch before creating).
|
|
106
|
+
5. **Set a repository flag** (for example GitHub Actions variable or GitLab CI variable via API) `MIRROR_HALTED=true` and have sync jobs check it at the start so you do not amplify divergence while humans fix it.
|
|
107
|
+
|
|
108
|
+
### Content to include in escalation messages
|
|
109
|
+
|
|
110
|
+
- Repository identifiers (both URLs or slugs).
|
|
111
|
+
- Branch (or ref) name.
|
|
112
|
+
- **OID on side A** and **OID on side B** (full hashes avoid ambiguity).
|
|
113
|
+
- **Pipeline run URL** and job name.
|
|
114
|
+
- Short **runbook pointer** (internal doc link) for “how we pick canonical history” and “who may force-push if ever.”
|
|
115
|
+
|
|
116
|
+
### After humans resolve divergence
|
|
117
|
+
|
|
118
|
+
Runbook should require:
|
|
119
|
+
|
|
120
|
+
1. Agreement on which tip (or merged result) is correct.
|
|
121
|
+
2. A single deliberate push to the **canonical** remote (or fast-forward merge on one side then sync).
|
|
122
|
+
3. Clear `MIRROR_HALTED` reset and verification that both tips match before re-enabling bidirectional automation.
|
|
123
|
+
|
|
124
|
+
## Implementation sketches (non-prescriptive)
|
|
125
|
+
|
|
126
|
+
### GitHub Actions (push to GitLab)
|
|
127
|
+
|
|
128
|
+
- Trigger: `on: push` for selected branches.
|
|
129
|
+
- Steps: clone with depth sufficient for ancestry check (or full clone for simplicity), add GitLab remote with token in secret, `fetch` GitLab, compare OIDs / ancestry, `git push` only on fast-forward.
|
|
130
|
+
- On divergence or push failure: exit 1 and call a small script that opens an issue or posts to Slack.
|
|
131
|
+
|
|
132
|
+
### GitLab push mirror to GitHub
|
|
133
|
+
|
|
134
|
+
- Configure in GitLab project settings; use a GitHub **fine-grained PAT** or deploy key with write access to the target repo.
|
|
135
|
+
- Complement with **branch protection** on GitHub and monitoring for mirror errors in GitLab.
|
|
136
|
+
|
|
137
|
+
### Webhook-driven bidirectional
|
|
138
|
+
|
|
139
|
+
- Each side receives push webhooks; each handler runs the same **guarded** sync script (SHA short-circuit, bot skip, fast-forward only, escalate on divergence).
|
|
140
|
+
- **Do not** run two independent scripts that both force-push without shared policy.
|
|
141
|
+
|
|
142
|
+
## Summary
|
|
143
|
+
|
|
144
|
+
- **Best default:** one canonical remote, **one-way** mirror with fast-forward-only pushes and OID short-circuit.
|
|
145
|
+
- **Bidirectional** is possible with **layered guards** (ancestor checks, bot identity, concurrency, no force).
|
|
146
|
+
- **Escalate automatically** on divergence and non-FF: fail visibly, notify, optionally open issues and halt further sync until humans reconcile.
|
|
147
|
+
|
|
148
|
+
## References
|
|
149
|
+
|
|
150
|
+
- [GitLab: Repository mirroring](https://docs.gitlab.com/ee/user/project/repository/mirror/)
|
|
151
|
+
- [GitHub Actions: Workflow triggers](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows)
|
|
152
|
+
- [GitLab: Deploy keys](https://docs.gitlab.com/ee/user/project/deploy_keys/)
|
|
153
|
+
- [GitLab: Project access tokens](https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html)
|
|
154
|
+
- [GitHub: Fine-grained personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)
|