Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7b2f415
feat(auth): gate Gravity behind landing page (GN-126)
tyemirov Jan 23, 2026
06d1388
feat(domain): introduce LandingAuthAttributes smart constructor; move…
tyemirov Jan 23, 2026
5567402
feat(frontend): introduce mpr-ui static loader; move auth wiring to c…
tyemirov Jan 23, 2026
d7a702c
feat(frontend): introduce MprUiCdnAssets smart constructor; move vali…
tyemirov Jan 23, 2026
438deb4
feat(frontend): allow runtime google client id override; validate at …
tyemirov Jan 23, 2026
4822667
Fix GN-434 sync end-to-end focus race
tyemirov Jan 24, 2026
a80d927
Document GN-434 resolution
tyemirov Jan 24, 2026
025788b
Fix GN-435 htmlView anchor drift
tyemirov Jan 25, 2026
71d8899
Fix GN-438 runtime Google client ID
tyemirov Jan 26, 2026
d83378f
Merge origin/bugfix/GN-438-runtime-google-client-id
tyemirov Jan 26, 2026
25b2d2f
Fix GN-439 IndexedDB persistence
tyemirov Jan 26, 2026
d2832a6
Fix GN-439 test harness constant
tyemirov Jan 26, 2026
bfb8ad0
Load tauth.js via CDN and untrack env files
tyemirov Jan 27, 2026
d02b41b
Load mpr-ui via runtime config
tyemirov Jan 27, 2026
b1b85ba
Fail fast on auth boot and pre-init GIS
tyemirov Jan 27, 2026
8cb24ed
Fix GN-442 auth boot and gHTTP /me proxy
tyemirov Jan 27, 2026
ffc6643
Align login button auth wiring
tyemirov Jan 27, 2026
d402cc2
feat(auth): adopt mpr-ui config.yaml loader
tyemirov Jan 27, 2026
e9ebcfa
fix(auth): pre-init tauth before mpr-ui login
tyemirov Jan 27, 2026
cf24d07
chore(dev): align ghttp/tauth config with demo
tyemirov Jan 27, 2026
4308804
fix(auth): serialize concurrent auth operations to prevent race condi…
tyemirov Jan 28, 2026
173da0a
chore(deps): bump mpr-ui to v3.6.2
tyemirov Jan 28, 2026
a0f26f7
fix(tests): resolve TAuth harness timing issues and update page-separ…
tyemirov Jan 29, 2026
dbf30ec
Shortcuts for docker orchestrtaion
tyemirov Jan 29, 2026
a48f65f
Fix GN-447 auth bootstrap and login E2E
tyemirov Jan 30, 2026
d7e27c3
feat(ui): enforce fullscreen menu-only toggle and tests
tyemirov Jan 30, 2026
c8d5c38
Fix GN-450 CDN-only mpr-ui test harness
tyemirov Jan 30, 2026
df527a5
Enable CI fail-fast and stabilize landing login test
tyemirov Jan 30, 2026
f8f67c0
Add fail-fast CLI switch for test harness
tyemirov Jan 30, 2026
92ec9b3
Add fail-fast flag to CI frontend tests
tyemirov Jan 30, 2026
641a891
Merge pull request #191 from MarcoPoloResearchLab/bugfix/GN-446-mpr-u…
tyemirov Jan 30, 2026
ba18bbd
Merge pull request #190 from MarcoPoloResearchLab/bugfix/GN-442-tauth…
tyemirov Jan 30, 2026
748ee70
Merge pull request #189 from MarcoPoloResearchLab/bugfix/GN-439-stora…
tyemirov Jan 30, 2026
81f5959
Merge pull request #186 from MarcoPoloResearchLab/bugfix/GN-438-runti…
tyemirov Jan 30, 2026
e7f7b36
Merge pull request #185 from MarcoPoloResearchLab/improvement/GN-437-…
tyemirov Jan 30, 2026
d81cbf2
Merge pull request #184 from MarcoPoloResearchLab/improvement/GN-436-…
tyemirov Jan 30, 2026
a4801fb
Install Playwright browsers in CI
tyemirov Jan 30, 2026
5719876
feat(tests): replace playwright login coverage with puppeteer
tyemirov Jan 30, 2026
35e4f08
Fix landing auth session redirect
tyemirov Jan 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .env.tauth.example

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/frontend-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,5 @@ jobs:
working-directory: frontend

- name: Run tests
run: npm test
run: npm test -- --fail-fast
working-directory: frontend
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ tools/
# Managed by gix gitignore workflow
.env
.env.*
!.env.*.example
.DS_Store
qodana.yaml
bin/
16 changes: 10 additions & 6 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ EasyMDE produces markdown, marked renders it to HTML, and DOMPurify sanitises th

**Storage, Configuration, and Auth**

- `GravityStore` persists notes in `localStorage` for offline-first behaviour; reconciliation applies backend snapshots.
- `GravityStore` persists notes in IndexedDB for offline-first behaviour; reconciliation applies backend snapshots.
- `createNoteRecord` validates note identifiers/markdown before writes so malformed payloads never hit storage.
- `GravityStore.setUserScope(userId)` switches the storage namespace so each Google account receives an isolated notebook.
- Runtime configuration loads from environment-specific JSON files under `data/`, selected according to the active hostname. Each profile now surfaces `authBaseUrl` so the frontend knows which TAuth origin to contact when requesting `/auth/nonce`, `/auth/google`, and `/auth/logout`.
- Authentication flows through Google Identity Services + TAuth: the browser loads `authBaseUrl/tauth.js`, fetches a nonce from `/auth/nonce`, exchanges Google credentials at `/auth/google`, and refreshes the session via `/auth/refresh`. The frontend never sends Google tokens to the Gravity backend; every API request simply carries the `app_session` cookie minted by TAuth and validated locally via HS256.
- Runtime configuration loads from environment-specific JSON files under `data/`, selected according to the active hostname. Each profile now surfaces `authBaseUrl` (API origin), `tauthScriptUrl` (TAuth CDN host), and `mprUiScriptUrl` (mpr-ui CDN host) so the frontend knows where to call `/auth/*` and where to load `tauth.js` + the auth UI bundle.
- Authentication flows through Google Identity Services + TAuth: the browser loads `tauth.js` from `tauthScriptUrl`, fetches a nonce from `/auth/nonce`, exchanges Google credentials at `/auth/google`, and refreshes the session via `/auth/refresh`. The frontend never sends Google tokens to the Gravity backend; every API request simply carries the `app_session` cookie minted by TAuth and validated locally via HS256.
- The backend records a canonical user table (`user_identities`) so each `(provider, subject)` pair (for example `google:1234567890`) maps to a stable Gravity `user_id`. That allows multiple login providers to point at the same notebook without rewriting note rows.

#### Frontend Dependencies
Expand Down Expand Up @@ -139,7 +139,9 @@ Profiles live under `frontend/data/runtime.config.<environment>.json` and are se
{
"environment": "development",
"backendBaseUrl": "http://localhost:8080",
"llmProxyUrl": "http://localhost:8081/v1/gravity/classify"
"llmProxyUrl": "http://localhost:8081/v1/gravity/classify",
"tauthScriptUrl": "https://tauth.mprlab.com/tauth.js",
"mprUiScriptUrl": "https://cdn.jsdelivr.net/gh/MarcoPoloResearchLab/mpr-ui@latest/mpr-ui.js"
}
```

Expand All @@ -148,15 +150,17 @@ Profiles live under `frontend/data/runtime.config.<environment>.json` and are se
{
"environment": "production",
"backendBaseUrl": "https://gravity-api.mprlab.com",
"llmProxyUrl": "https://llm-proxy.mprlab.com/v1/gravity/classify"
"llmProxyUrl": "https://llm-proxy.mprlab.com/v1/gravity/classify",
"tauthScriptUrl": "https://tauth.mprlab.com/tauth.js",
"mprUiScriptUrl": "https://cdn.jsdelivr.net/gh/MarcoPoloResearchLab/mpr-ui@latest/mpr-ui.js"
}
```

When serving from an alternate hostname, add a new profile or override the URLs explicitly before bootstrapping the Alpine application.

#### Authentication Contract

- **Browser responsibilities:** Gravity’s frontend loads `authBaseUrl/tauth.js`, asks `/auth/nonce` for a nonce, exchanges Google credentials at `/auth/google`, and retries requests via `/auth/refresh` when the backend returns `401`. All network calls simply include the `app_session` cookie; no Google tokens touch the Gravity API.
- **Browser responsibilities:** Gravity’s frontend loads `tauth.js` from `tauthScriptUrl`, asks `/auth/nonce` for a nonce, exchanges Google credentials at `/auth/google`, and retries requests via `/auth/refresh` when the backend returns `401`. All network calls simply include the `app_session` cookie; no Google tokens touch the Gravity API.
- **Backend responsibilities:** the API validates `app_session` with the shared HS256 signing secret and fixed `tauth` issuer, stores no refresh tokens, and trusts the canonical `user_id` resolved by the `user_identities` table. A one-time migration strips the legacy `google:` prefix from existing note rows and backfills the identity mapping automatically.
- **Logout propagation:** triggering **Sign out** in the UI invokes `/auth/logout`, revokes refresh tokens inside TAuth, and dispatches `gravity:auth-sign-out` so the browser returns to the anonymous notebook.
- **Future providers:** because every `(provider, subject)` pair maps to the same Gravity user, we can add Apple/email sign-in later without rewriting stored notes.
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,26 @@ and are grouped by the date the work landed on `master`.
- Background version watcher polls a manifest and reloads the app when a new deploy ships so browsers never run stale code (GN-206).

### Changed
- Frontend persistence now uses IndexedDB with a localStorage migration and BroadcastChannel refreshes (GN-439).
- Full-screen toggle now lives inside the avatar menu with updated exit icon strokes and a text label (GN-207).
- Auth header now shows only the signed-in display name to avoid exposing email addresses (GN-208).
- TAuth session now delegates nonce issuance and credential exchange to auth-client helpers instead of local fetches (GN-423).
- Centralized environment config defaults and reused them across runtime config and test harnesses (GN-427).
- Runtime config now returns a frozen app config and callers pass it explicitly instead of shared globals (GN-427).
- Environment example files now live at `env.*.example`, keeping `.env*` files untracked while preserving copy-ready templates (GN-443).
- mpr-ui now loads from a runtime-configured script URL (`mprUiScriptUrl`) after tauth.js so login components always register (GN-444).
- Auth boot now fails fast when required helpers/components are missing and pre-initializes Google Identity Services before rendering the login button to avoid GSI warnings (GN-445).
- Signed-out visitors now see a landing page with a Google sign-in button; the Gravity interface requires authentication (GN-126).
- mpr-ui now loads from a static script tag and auth components mount after runtime config so attributes are applied before initialization (GN-436).
- Frontend now pulls mpr-ui assets from the `@latest` CDN tag so releases stay aligned (GN-437).

### Fixed
- TAuth helper now loads from a dedicated CDN URL via `tauthScriptUrl`, and gHTTP no longer proxies `/tauth.js` while proxying `/me` to TAuth for session checks in the dev stack (GN-442).
- Dev docker compose now serves Gravity over HTTPS at computercat.tyemirov.net:4443 via gHTTP proxies for backend/TAuth endpoints, with updated dev runtime config and env templates (GN-441).
- Normalized development runtime config endpoints to swap loopback hosts for the active dev hostname and refreshed the TAuth env example for localhost defaults (GN-440).
- Sync queue now coalesces per note and resolves payloads from the latest stored note to avoid duplicate ops and offline failures (GN-439).
- Landing sign-in now sets mpr-ui auth base/login/logout/nonce attributes so nonce requests hit TAuth instead of the frontend origin (GN-433).
- Runtime config now accepts a Google client ID override so local GSI origins can match the correct project (GN-438).
- Updated the TAuth helper loader and harness to use `/tauth.js`, keeping Gravity aligned with current TAuth builds (GN-424).
- Expanded edit-height locks now override CodeMirror auto sizing so expanded cards keep their height in edit mode (GN-425).
- TAuth runtime config now forwards `authTenantId` into the loader/session bridge and drops the crossOrigin attribute so tauth.js loads cleanly in stricter CORS setups (GN-426).
Expand Down Expand Up @@ -50,6 +63,9 @@ and are grouped by the date the work landed on `master`.
- Expand/collapse toggles now align to the full card width rather than the text column, with resize-aware positioning and mobile regression coverage (GN-307).
- Clicking the card control column now finalizes inline editing without flickering back to markdown mode, covered by a regression targeting the GN-308 scenario (GN-308).
- Puppeteer sync persistence tests now ensure backend session cookies attach (with a request-interceptor fallback for file:// origins), stabilizing multi-iteration runs (GN-432).
- Sync end-to-end coverage now waits for the authenticated shell and CodeMirror input before typing to avoid focus races (GN-434).
- Expanded htmlView checkbox toggles now preserve viewport anchors and skip redundant re-renders to prevent drift (GN-435).
- Runtime config now requires an explicit Google client ID so GIS matches the configured origin and the landing sign-in button renders (GN-438).

### Documentation
- Folded `MIGRATION.md` into `ARCHITECTURE.md`, clarifying event contracts and module guidance (GN-54).
Expand Down
32 changes: 32 additions & 0 deletions ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Each issue is formatted as `- [ ] [GN-<number>]`. When resolved it becomes -` [x
- The grid remains visually stable; no unexpected reflow or scrollbars appear.
- Existing editing behavior (inline edit, blur save, no-jump UX) is unchanged.
- Tests pass via the standard `make test` workflow.
- [x] [GN-126] (P0) Gate Gravity behind authentication and show a landing page when signed out.
Require login to view the Gravity interface; unauthenticated visitors see a landing page with a Google sign-in button instead. (Resolved by adding landing UI + mpr-ui auth wiring, updating tests, and stabilizing the harness to keep multi-iteration runs green.)


## Improvements (202–299)
Expand Down Expand Up @@ -156,6 +158,36 @@ Each issue is formatted as `- [ ] [GN-<number>]`. When resolved it becomes -` [x
- `tools/gravity/frontend/tests/helpers/backendHarness.js`: stop passing `GRAVITY_TAUTH_ISSUER` or set it to the default internally.
- [x] [GN-432] Intermittent `persistence.sync.puppeteer.test.js` failures during multi-iteration `make ci` runs.
(Resolved by verifying backend session cookies attach in Puppeteer and falling back to injecting Cookie headers per backend request when file:// origins reject setCookie; multi-iteration frontend suites now stay stable.)
- [x] [GN-433] Landing auth error because the login button is configured with tauth-* attributes instead of mpr-ui base/login/logout/nonce attributes, causing /auth/nonce to hit the frontend origin and fail. (Resolved by wiring the base/login/logout/nonce attributes alongside tauth fields so the mpr-ui login button uses TAuth endpoints.)
- [ ] [GN-434] (P2) `sync.endtoend.puppeteer.test.js` timed out waiting for `.markdown-block:not(.top-editor)[data-note-id]` during baseline `make test` runs; investigate the flake.
Observed again during GN-438 `make test`; rerun passed.
- [ ] [GN-435] (P2) `htmlView.checkmark.puppeteer.test.js` intermittently fails the anchored-card assertion during `make ci` (observed ~28px drift).
- [x] [GN-436] (P1) Simplify mpr-ui loading by including the bundle in `frontend/index.html` and mounting auth components with runtime-configured attributes before initialization.
(Resolved by loading mpr-ui via a static script tag, cloning auth elements from templates after runtime config, and applying auth attributes before mounting.)
- [x] [GN-437] (P1) Load mpr-ui assets from the `@latest` CDN tag to keep the frontend in sync with upstream releases.
(Resolved by switching the frontend CDN links and test harness mirrors to `@latest`.)
- [x] [GN-438] (P1) Allow runtime config to override the Google client ID so local dev origins can match the correct GSI project. (Resolved by requiring googleClientId in runtime config payloads/JSON, plumbing through the config builder, and updating tests.)
- [x] [GN-439] (P1) `sync.manager.test.js` fails during `make ci` with `offline` errors and duplicate sync operations (expected 1, observed 2) in the "coalesces repeated upserts" case; investigate and stabilize. (Resolved by coalescing per-note operations, deferring payload hydration to flush time, and adding regression coverage.)
- [x] [GN-439] (P1) `QuotaExceededError` in sync queue persistence when localStorage fills; redesign persistence to avoid localStorage quotas and coalesce pending sync operations. (Resolved by migrating notes/sync queue/sync metadata to IndexedDB with localStorage-only test mode and storage-full notifications.)
- [x] [GN-440] (P1) Local auth fails when TAuth tenant origin/cookie domain drift from the runtime-configured localhost URLs, causing tauth.js/nonce requests to miss or fail CORS. (Resolved by rewriting loopback runtime endpoints for non-loopback dev hosts and refreshing TAuth env defaults for localhost.)
- [x] [GN-441] (P1) Dev auth fails on computercat.tyemirov.net because gHTTP still serves only the frontend and runtime config points to localhost; proxy backend/TAuth through gHTTP HTTPS and update dev config/env defaults. (Resolved by adding gHTTP HTTPS env config + proxy routes, updating compose and runtime config defaults for computercat, and documenting the new dev stack.)
- [x] [GN-442] (P1) Load tauth.js from the CDN (not via gHTTP), remove the /tauth.js proxy route, and wire the runtime config/test harness to use a dedicated tauthScriptUrl for the helper. (Resolved by enforcing absolute CDN tauth.js URLs and proxying /me through gHTTP so auth boot hits TAuth.)
- [x] [GN-443] (P1) Keep `.env*` files untracked and rename example env files to `env.*.example` with updated setup docs.
- [x] [GN-444] (P1) Ensure the mpr-ui login component always registers by loading the bundle from a runtime-configured `mprUiScriptUrl` after tauth.js.
- [x] [GN-445] (P1) Make auth boot strict (no fallbacks) and pre-initialize GIS before rendering the mpr-ui login button to avoid GSI warnings.
- [ ] [GN-446] (P1) Adopt the mpr-ui config.yaml loader for auth wiring so login buttons render from declarative config and tauth.js only loads from the CDN.
- [x] [GN-447] (P1) Auth boot loop and missing avatar after login when TAuth init resolves late.
Resolved by waiting for TAuth init callbacks before dispatching auth state, aligning landing/app redirects, and adding Playwright E2E coverage for login → app user menu rendering.
- [x] [GN-448] (P0) Enter full screen menu item should appear before Sign out in the user menu.
(Resolved by adding the full screen action to the mpr-user menu items ahead of logout, wiring the menu action to the full screen toggle, and extending avatar menu coverage to assert ordering.)
- [x] [GN-449] (P0) Enter full screen must be a user menu item before Sign out (regression reported again).
(Resolved by removing the standalone fullscreen button, keeping the menu item before Sign out, and adding Playwright coverage for menu toggling + absence of the standalone control.)
- [x] [GN-450] (P1) CI fails because the Playwright login test reads mpr-ui assets from the gitignored tools folder.
(Resolved by removing local mpr-ui fixtures in the test harness, waiting for mpr-ui config application in landing tests, and propagating nonce error codes through the auth wrapper; make test/lint/ci pass.)
- [x] [GN-451] (P1) Fail fast on CI test runs after the first failure while keeping local runs exhaustive.
(Resolved by enabling CI-only fail-fast behavior in the test harness with immediate error output.)
- [x] [GN-452] (P1) Landing page should redirect to app when a valid session exists even if mpr-ui does not emit auth events on load.
(Resolved by bootstrapping the TAuth session during landing startup and adding a landing E2E test that stubs mpr-ui.)


## Maintenance (428–499)
Expand Down
Loading