Releases: Suydev/isotope-code
v3.3.7 — Auth-gated sync state machine; stop infinite retry on auth failure
[3.3.7] — 2026-06-08 — Fix: auth-gated sync state machine; stop infinite retry on auth failure
Fixed (sync state machine — complete rebuild)
- Auth failure is now a STOP condition, not a retry condition — Previously any auth error (expired session, no session, 401) caused the sync to be written as
failedand retried on the next timer tick, visibility change, or online event. The same 2009 KB payload would upload infinitely. Now any auth error immediately sets__isoSyncAuthBlocked = trueand the entire sync pipeline halts. - New
isAuthError/isPermissionError/isNetworkErrorclassifiers — Errors are classified before deciding to stop vs. retry. Network errors still retry; auth and permission errors do not. authedJsonnow throws taggedAuthErrorobjects — When JWT is null or refresh fails, the thrownErrorhas__isAuthError = true. When the server returns 401/auth message, the thrown error is also tagged. All callers can now distinguish the error type.- 30-min timer stops on auth failure, restarts on recovery —
__isoSyncAuthBlock()callsclearInterval(_autoSyncTimer).__isoSyncAuthUnblock()restarts it and schedules one sync attempt. - All sync triggers check auth-blocked state —
__isoAutoSync,__isoStartupSync, the 30-min timer interval, the visibility-change handler, and the online-event handler all checkwindow.__isoSyncAuthBlockedand return{ reason: 'paused_auth' }without running any upload/download. - Token intercept unblocks sync on new valid session — When Supabase returns a new
access_token(login, token refresh), the fetch interceptor callswindow.__isoSyncAuthUnblock(), which clears the blocked flag, restarts the timer, and queues one sync attempt 2 s later. - Login (
__isoLogin) unblocks sync on success — After a successful username/password login and profile sync,__isoSyncAuthUnblock()is called so sync resumes without waiting for the next Supabase token intercept. - Online event re-validates session before unblocking — When the network comes back and
__isoSyncAuthBlockedis true, the handler callsgetValidJwt()first; if a valid JWT exists, it unblocks and syncs. It does not blindly retry the upload. - Smart-sync catch re-throws auth errors — The
try/catcharound/__auth/backup/latestin__isoRunManualCloudSyncpreviously swallowed all errors as "non-fatal". Now auth errors are rethrown so they propagate to the outer catch and trigger the block. - Permission errors get a distinct
failed_permissionstatus — These are written to sync metadata and history separately; they never trigger a retry. - All sync operations (
snapshot,upload,download_import,manual_sync) handle auth errors uniformly — Each catch block calls__isoSyncAuthBlock()and writespaused_authto sync history instead offailed.
Audit (v3.3.7 — 2026-06-08)
| # | Check | Result |
|---|---|---|
| 1 | Auth failure stops all scheduled sync | ✅ fixed |
| 2 | Same payload never uploads infinitely on auth error | ✅ fixed |
| 3 | 30-min timer cleared on auth failure | ✅ fixed |
| 4 | Timer restarted on new valid session | ✅ fixed |
| 5 | All sync triggers check __isoSyncAuthBlocked |
✅ fixed |
| 6 | Token intercept calls __isoSyncAuthUnblock() |
✅ fixed |
| 7 | Login success calls __isoSyncAuthUnblock() |
✅ fixed |
| 8 | Auth vs network vs permission errors classified | ✅ fixed |
| 9 | Smart-sync auth errors propagate instead of being swallowed | ✅ fixed |
v3.3.6 — Cloud sync download fix; storage cleanup; setup improvements
[3.3.6] — 2026-06-08 — Fix: cloud sync download on new device; storage cleanup; setup improvements
Fixed
- CRITICAL: Cloud backup never downloaded on new/different device —
GET /__auth/backup/latestreturned the backup JSON but omitted thecloud_snapshotfield when serving from theexports/latest.jsonpath (the primary path). The client smart-sync readscloudData.cloud_snapshot.exported_atto computecloudTs. Without it,cloudTs = 0so thecloudIsNewercheck (!localTs && cloudTs > 0) was alwaysfalseon a new device — the cloud backup was fetched but never applied. Fix: the endpoint now also fetchescloud-snapshot/latest.jsonand includes it in the response on every path. - CRITICAL: Download skipped on startup (timing race) —
window.__isoBuildBackupandwindow.__isoApplyBackupare registered by the app bundle's internal sync method, which fires asynchronously after React initialises. The startup sync (previously 5 s after page load) frequently fired before these were set, falling back to an upload-only snapshot and never downloading cloud data. Fix: startup delay increased to 8 s;__isoAutoSyncnow polls for the functions for up to 15 additional seconds before falling back, giving the app enough time to register them. - First-sync debounce blocks retry — If the startup sync fell back to upload-only on a new device (no download), the 5-minute debounce was already written, preventing retry on the next page load. Fix: when a first-sync (no local snapshot history) completes without a download, the debounce timestamp is cleared so the next page load retries.
- New-device fallback bootstraps from cloud — In the upload-only fallback path, if the device has no local sync history, the server now calls
/__auth/bootstrapfirst to restore the cloud snapshot into Supabase DB before uploading a snapshot. This ensures the snapshot reflects actual cloud state even when build/apply fns were never registered.
Fixed (storage)
- Old cloud backup files accumulating in Storage —
uploadRawUserBackupJsonwrote a new timestamped file ({userId}/exports/YYYY-MM-DD....json) on every upload but never deleted old ones. Fixed: after each upload, old timestamped files in the same folder are pruned in the background, keeping only the 3 most recent. - Cloud snapshot history files accumulating —
uploadCloudSnapshotForUserwrote history snapshots ({userId}/cloud-snapshot/history/....json) that were never cleaned up. Fixed: after each history write, files are pruned keeping only the 5 most recent. - New
supaStorageListAsUserhelper — ImplementsPOST /storage/v1/object/list/{bucket}using user-scoped JWT + anon key, used by the new pruning logic.
Improved (setup)
setup.shinstalls Node.js 18+ on Debian/Ubuntu via NodeSource — Previously usedapt-get install nodejswhich installs the distro-packaged version (often v12). Now checks the installed version first; if < 18, fetches the NodeSource v22 setup script and uses that, falling back tonvmor a clear error.setup.shworks non-interactively —--yes/-yflag and piped stdin now skip all prompts reliably.
Audit (v3.3.6 — 2026-06-08)
| # | Check | Result |
|---|---|---|
| 1 | cloud_snapshot included in all /__auth/backup/latest responses |
✅ fixed |
| 2 | Startup sync polls up to 15 s for build/apply fns before fallback | ✅ fixed |
| 3 | First-sync debounce cleared when download didn't happen | ✅ fixed |
| 4 | Fallback path calls /__auth/bootstrap on new device |
✅ fixed |
| 5 | Old export files pruned after upload (keep 3) | ✅ fixed |
| 6 | Old snapshot history files pruned after upload (keep 5) | ✅ fixed |
| 7 | supaStorageListAsUser helper implemented |
✅ added |
IsotopeAI v3.3.5 🧪
IsotopeAI v3.3.5
Fixed
- CRITICAL:
/api/health?_=<timestamp>speed probe returned HTTP 404 — The network speed probe fired by the app at every session used a cache-busting query-string (?_=Date.now()). The API health handler matchedreq.url === '/api/health'(exact string), so any request with a query string silently fell through to the/api/*404 fence added in v3.3.4. The speed probe received{"ok":false,"error":"Not found"}(34 B) instead of the ~230 B health payload, making the sync timeout calculator classify every user as "slow" (150 s timeout). Fixed: all/api/*route handlers now matchadminPath(the URL parsed without query string) instead of the rawreq.url. - Same query-string fallthrough for
/api/version,/api/check-update,/api/ai-config— All four API route handlers were patched fromreq.url ===toadminPath ===.
Performance
- Health endpoint caching (15 s TTL) —
/api/healthpreviously made 3 concurrent Supabase HTTP round-trips on every call (REST, Auth, Storage), taking 200–600 ms. Results are now cached for 15 seconds; subsequent calls return in <1 ms. The cache is only populated on a successfulokresponse so degraded states still probe live. - Pre-gzip bundle cache — 10 major JS bundles (App, Auth, Focus, Onboarding, SingleGroup, Leaderboard, Settings, AppAccessGate, SessionSync, Invites) are gzip-compressed once at server startup and stored in memory (
_gzipCache). All subsequent requests for these assets skip the per-requestzlib.gzip()call and serve the cached compressed buffer instantly. For all other hashed immutable assets and SW files, the first gzip result is also cached. - 14 missing database indexes added (
performance-indexes.sql) — Static schema analysis found 14 unindexed foreign-key and date-ordering columns:community_events(creator_id),community_events(host_user_id)— FK columns never indexedcommunity_events(created_at),community_events(updated_at)— date orderinguser_tours(user_id)— FK for guided-tour lookups per useruser_tours(created_at),user_tours(updated_at)— date orderinguser_roles(granted_by)— FK for admin audit queriesgroup_invites(created_at)— expiry + ordering queriesgroup_challenges(created_at)— orderinggroups(created_at),groups(updated_at)— orderingusers(updated_at)— profile sync delta queries- Apply via:
Supabase Dashboard → SQL Editor → run performance-indexes.sql
Added
performance-indexes.sql— New file containing all 14CREATE INDEX IF NOT EXISTSstatements. Idempotent and safe to run multiple times. Referenced from admin panel setup guide.
Audit (v3.3.5 — 2026-06-07)
| # | Check | Result |
|---|---|---|
| 1 | /api/health?_=timestamp speed probe now returns 200 JSON |
✅ fixed (adminPath match) |
| 2 | /api/version, /api/check-update, /api/ai-config same fix |
✅ all 4 handlers patched |
| 3 | Health endpoint cached at 15 s TTL | ✅ <1 ms on cache hit |
| 4 | 10 bundles pre-gzip'd at startup | ✅ _gzipCache Map |
| 5 | 14 missing indexes documented in performance-indexes.sql |
✅ new file |
| 6 | All changes pushed to GitHub | ✅ |
📥 Quick Install
git clone https://github.com/Suydev/isotope-code.git
cd isotope-code
bash setup.sh📋 What's Changed
See CHANGELOG.md for the full history.
❤️ Support
UPI: 9699393886@fam · isotopeai.in
v3.3.0 — Live DB RLS patch, GitHub Pages docs link, login error improvements
What's new in v3.3.0
Fixed
- §5+§6 RLS policies applied to live Supabase DB — Own-row RLS optimisation (§5) and leaderboard public-read policies (§6) from
performance-patch.sqlapplied to the live database via the Management API. Leaderboard now works correctly for authenticated users. Tables patched: users, user_profiles, user_points, user_stats_summary, daily_user_stats, study_sessions_log, notifications, user_presence, groups, group_members, group_chat_messages, group_challenges, group_challenge_participants, group_announcements, group_invites, group_milestones. - Improved login error messages (
/__auth/login) — Login failures now surface the specific Supabase error: "email not confirmed" shows a confirmation-link hint; "invalid credentials" shows a clear message directing users to their Supabase-registered email and password. Previously all failures returned a generic message.
Added
- GitHub Pages docs badge on login screen (
server.mjsDOCS_LINK_HTML) — A📖 Docsbadge is injected into every served HTML page (including the unauthenticated login screen) in the bottom-right corner, linking to https://suydev.github.io/isotope-code/ - README updated — Logo in README header now links to GitHub Pages; Docs badge and nav link added to the header; footer updated with Documentation link; version badge updated to 3.3.0.
- GitHub Pages docs updated (
docs/index.md) — Added v3.3.0 changelog section, GitHub Pages self-link, repository link, and updated version footer.
Audit summary
| Check | Result |
|---|---|
| Total assets scanned | 211 (191 non-font) |
/api/broadcast in vendor-supabase bundle |
✅ Supabase realtime internal — not a server route |
All /__auth/* server endpoints |
✅ login, signup, backup, backup/latest, snapshot, profile, refresh, delete-account |
All /api/* server endpoints |
✅ version, healthz, status, config, export, ai/*, pwa-events, proxy |
| §5 own-row RLS applied to live DB | ✅ 6 batches × HTTP 201 |
| §6 leaderboard public-read policies | ✅ stats_select_all, daily_select_all, users_select_display |
| Missing tables (user_inventory, community_events) | ℹ️ Not in live schema — SQL skipped safely |
Full changelog: https://github.com/Suydev/isotope-code/blob/main/CHANGELOG.md
Documentation: https://suydev.github.io/isotope-code/
v3.2.0 — Leaderboard RLS fix + SQL index correction
What changed
Fixed
- Leaderboard shows empty rankings — All 5 leaderboard REST queries now use the authenticated user JWT instead of the bare ANON key. The §5 RLS hardening had set
user_stats_summary,daily_user_stats, andusersto "own row only"; the ANON key hasauth.uid() = NULLso every query returned 0 rows. Authenticated users now see full community rankings. - SQL index creation fails silently —
performance-patch.sql §4referenced a columnstudy_minutesthat does not exist ondaily_user_stats(correct name:seconds_studied). The indexidx_daily_user_date_minutessilently failed on every fresh install. Corrected.
Added
performance-patch.sql §6— Three new leaderboard-compatible RLS policies:stats_select_all(authenticated users can SELECT all rows inuser_stats_summary),daily_select_all(authenticated users can SELECT all rows indaily_user_stats),users_select_display(anyone can SELECTid, username, name, avatar_urlfromusers). Own-row write restrictions from §5 remain in effect.
Upgrading
- Pull the latest code:
git pull - Apply the updated
performance-patch.sqlin Supabase SQL Editor (the §6 block at the end adds the three new policies — safe to re-run, all statements are idempotent). - Restart the local server:
isotope restart
v3.1.3 — Performance Hardening & Professional Release
What's New in v3.1.3
🛡️ Supabase RLS Performance Hardening (Advisor Fix)
All 20+ Row-Level Security policies now use (SELECT auth.uid()) instead of auth.uid() directly. This is the #1 Supabase Performance Advisor recommendation — it forces PostgreSQL to evaluate the auth function once per query rather than once per row, eliminating a major source of RLS overhead on every authenticated read.
⚡ PWA Server-Check Polling Fixed
public/pwa-local.js replaced the aggressive 10-second setInterval server poll with a visibility-change listener and a 5-minute background keepalive. The server is now checked only when the user returns to the tab, not constantly in the background.
📁 .env.example Added
The .env.example file was missing entirely — causing setup.sh and setup.bat to fail on fresh clones. It is now included with all required and optional fields documented.
🪟 update.bat Added
Windows users now have an update.bat shortcut in the repo root that delegates to isotope update or the local wrapper.
📊 performance-patch.sql — Complete RLS Policy Replacement
Section §5 of performance-patch.sql now DROP/CREATE-replaces all affected policies with the optimised (SELECT auth.uid()) pattern. Safe to re-run.
How to Update
isotope updateFiles Changed
| File | Change |
|---|---|
.env.example |
Added (was missing) |
update.bat |
Added (Windows update shortcut) |
performance-patch.sql |
§5 added: full RLS auth.uid() → (SELECT auth.uid()) migration |
public/pwa-local.js |
10s polling → visibility-change + 5min keepalive |
.gitignore |
Added !.env.example exception |
CHANGELOG.md |
v3.1.3 entry |
README.md |
Badge updated to v3.1.3 |
VERSION |
Bumped to 3.1.3 |
package.json |
Version 3.1.3 |
v3.1.2 - Storage-backed backup restore
v3.1.2
- Connect Settings/Data & Privacy Cloud Sync to the real browser-generated backup JSON from the compiled exporter.
- Store the full backup JSON in user-content exports/latest.json and embed it into cloud-snapshot/latest.json as local_backup/backup_data.
- Restore local collections from Storage backup JSON through the compiled importer instead of querying missing tasks/subjects/focus_sessions/habits/exams/daily_logs/tests tables.
- Add cloud-snapshot fallback for /__auth/backup/latest when exports/latest.json is absent.
- Auto-import the cloud backup on empty/cache-cleared authenticated devices during app bootstrap.
- Update sync proof docs to mark local collections as Storage-backed, not fake DB table sync.
v3.1.1 - Real cloud backup pipeline
v3.1.1
- Upload the real browser-generated backup JSON from Settings/Data & Privacy sync flows into Supabase Storage under user-content.
- Download and import the latest full cloud backup during cloud restore instead of trusting local browser cache.
- Refresh canonical cloud snapshots after profile, onboarding, avatar, study session, and manual backup changes.
- Use deterministic avatar object paths with SHA-256 and clean older owned avatar objects to avoid repeated duplicates.
- Fix login persistence across local server restarts by restoring refresh-token sessions and enabling Supabase token auto-refresh.
- Tighten Storage RLS policies for avatars, user-content, and notes so authenticated users manage only their own paths.
- Add trace/proof documentation for cache-clear cloud restore checks.
IsotopeAI Local v2.9.0
IsotopeAI Local v2.9.0
This release packages IsotopeAI as portable local software: users run the app on their own device, while Supabase provides shared cloud sync for auth, profiles, onboarding, community, events, storage, leaderboard, notifications, and realtime.
Highlights
- Default public Isotope Supabase URL and anon key for normal installs.
- Normal users do not need service-role keys, admin secrets, Supabase PATs, or GitHub PATs.
- Smarter first-run setup scripts for Windows, macOS, Linux, and Termux.
- Safer update scripts that preserve .env and stash local changes.
- GitHub Pages docs rewritten for the local software model.
- Startup output now shows local app/cloud sync status instead of admin-disabled noise.
- Missing JS asset recovery from upstream /assets/.js sources.
- Onboarding/login and stale fake-subject protections remain included.
Verified
- node server.mjs starts in normal local app mode.
- Supabase REST/Auth/profile/onboarding/community-events are reachable.
- Storage buckets avatars, event-images, user-content, and notes exist.
- Upload, download, signed URL, and cleanup passed for all four buckets.
- /api/community-events returns widget-safe event data.
- Tracked-file secret scan is clean for service-role/admin/GitHub secrets.
Security
Repository history was previously reset to a sanitized root commit. Any token that was ever exposed should still be revoked and regenerated.