hub-client: connection-gated auth + local-first default, with offline editing of cached hub projects#397
Draft
shikokuchuo wants to merge 15 commits into
Draft
hub-client: connection-gated auth + local-first default, with offline editing of cached hub projects#397shikokuchuo wants to merge 15 commits into
shikokuchuo wants to merge 15 commits into
Conversation
Add buildRepo() that omits the WebSocket adapter when no syncServer is given; connect/createNewProject skip the peer wait in that mode. CreateProjectOptions.syncServer is now optional. Add SyncClient.flush() and flush-on-create so a local project survives an immediate reload.
createLocalProjectSet()/connectLocal() build a storage-only Repo (no WS adapter, no waitForPeer) via a shared buildRepo() helper; flush() and flush-on-disconnect keep local edits durable. ProjectSetEntry.syncServer is now optional (absent = local).
getOrCreateLocalActor() mints a stable 32-hex Automerge actor once per browser (UserSettings.localActorId) so local edits attribute to one coherent author across reloads instead of a fresh random per-handle stub. App wiring lands in A4.
…hdc) Remove the LoginScreen entry gate; sign-in is now an opt-in overlay via a header 'Connect to a hub' control. useProjectSet auto-creates a local project set on first run; create/open branch local vs hub by syncServer (local authored under the per-browser local actor). useAuthProbe and the auth-loss teardown are scoped to hub projects so a local project is never torn down.
Add e2e/local-first.spec.ts (real-browser: no login gate; create local project offline → navigate into editor → reload persists). Add LOCAL-FIRST.md user docs, cross-linked from README. The OIDC sign-in leg is not automated (no test IdP); manual path documented.
…u4p8xhdc) Logged-off selection of a hub project (with a syncServer) resolved a null actor (401) and the open path returned silently. Extract resolveActorForOpen: local projects always open; a hub project needing a session we don't have now prompts sign-in instead of a silent no-op.
Scopes full offline editing of cached hub projects as the adoption forward-switch+display-bridge run recurrently on each reconnect (local actor offline, HMAC on reconnect). Coordinated with D1 durability (bd-10bdjmjb); supersedes the interim prompt-sign-in for cached projects.
Reusable timeline fixtures for the offline-cached-hub epic (bd-xxjy9yfp): test-hub gains setHolding/dropConnections (stable-URL online→offline→online), cached-hub.ts composes them with fake-indexeddb, and a green baseline test exercises the full timeline. The baseline passing answers B4's open question: an offline edit to an existing cached doc syncs up on reconnect via normal automerge sync, no D1 fix needed. B1-B3 behavioral specs are staged as it.todo.
…-qklxdkwh) A logged-off/offline open of a *cached* hub project now falls back to the persisted local actor and opens from cache instead of prompting sign-in; an uncached project prompts sign-in when online or reports offline-unopenable. The openActor seam derives online-vs-offline from whether resolveHubActor resolves null (logged off) or throws a TypeError (offline). Cache membership is a new stateless isDocCached() probe in quarto-sync-client.
…b projects (B2-B5, bd-xxjy9yfp) Offline edits to a cached hub project author under the local actor with an identities row (B2, existing connect path). On reconnect, SyncClient.switchActor re-authors open handles to the server-trusted HMAC actor and bridges the identities so the offline+online timeline reads as one human; the App wires this via reconcileHubActorOnReconnect on onConnectionChange (B3). Offline edits to N existing cached docs all sync on reconnect via normal automerge sync, no D1 fix (B4). Docs + honest E2E status in LOCAL-FIRST.md and the plan (B5).
STATIC_PORT (and the baked-in VITE_DEFAULT_SYNC_SERVER for build:local-prod) now honor a PORT override, defaulting to 8080, so local-prod can run on a port other than the default (e.g. PORT=5173).
…bd-ivkf752c) A4 (bd-u4p8xhdc) removed this field, defaulting silently to the active project set's server. Restored per user request: the field is back in both forms, defaulting to DEFAULT_SYNC_SERVER and editable; clearing it still creates a local-only project.
…on (bd-lkjdu6fn) The field restored in bd-ivkf752c defaulted unconditionally to DEFAULT_SYNC_SERVER, which silently turned local creation into a hub-creation attempt with no session: the project got created and wired to a real WS adapter, then immediately torn down by the auth-loss-teardown effect — a flash of the editor before bouncing back to the selector, on create and on every reopen of that project. Now defaults to projectSetSyncServer ?? '' (empty/local when disconnected, the connected hub when connected), via a new newProjectSyncServer state reset on each Create/Import open. The Connect form's own field is unaffected.
…(bd-lficgfwi) Two halves of the same regression against B1 (bd-qklxdkwh): App's auth-loss teardown was state-based (!auth && hub project → disconnect), unmounting every logged-off cached-hub open on the next render; and createNewProject swallowed resolveActorId's null, silently creating a hub-wired project with no session for the teardown to kill. Teardown is now transition-based (shouldTeardownOnAuthChange, ref-tracked hadAuth); the sync client aborts creation with a typed ActorAuthRequiredError which App turns into a sign-in prompt; a consumed showLogin is reset on successful sign-in.
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes the hub client local-first, with authentication gated on connecting to a hub rather than on entry.
Local-first default
Connection-gated auth
Offline editing of cached hub projects
Also included
LOCAL-FIRST.mduser docs.PORT.Plans:
claude-notes/plans/2026-07-06-hub-client-connection-gated-local-first.mdand2026-07-15-hub-client-offline-cached-hub-projects.md(epic bd-xxjy9yfp), under the auth-reshape umbrella.