Add "secure by default" setup gate for fresh Grist installations (WIP)#2145
Add "secure by default" setup gate for fresh Grist installations (WIP)#2145
Conversation
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
|
Deployed commit |
Motivation: Fresh Grist installations currently start wide open with no authentication — anyone who can reach the URL has full access. After surveying how 20 popular self-hosted apps handle initial auth (hardcoded creds, first-visitor-wins, auth-off-by-default, etc.), this adopts a "not in service" posture inspired by Frigate/Pi-hole's "random key to logs" pattern, combined with getgrist.com sign-in for operators who have an account. What this does: - New installations show a setup page instead of serving documents - Three paths to get past the gate: 1. Set GRIST_ADMIN_EMAIL and sign in via getgrist.com (recommended) 2. Enter a boot key from server logs (for air-gapped deployments) 3. Set GRIST_IN_SERVICE=true to skip (YOLO mode) - Setup page also guides operators on sandboxing and backups - Boot key is auto-generated (24-char hex) on first activation and stored in the DB prefs column (no migration needed) - GRIST_BOOT_KEY env var overrides the DB value; empty string disables - Existing installations are not affected: if a real auth system is configured (anything other than "minimal" fallback), the gate is bypassed automatically What's next (not in this commit): - Wire up actual getgrist.com sign-in flow from the setup page - Allow toggling GRIST_IN_SERVICE from the admin panel - Add heuristics for existing installations (e.g. installation age, existing users) to avoid regression into "not in service" state - Boot key disable/regenerate from admin panel - i18n for setup page text Server changes: - FlexServer.addSetupGate(): middleware that checks _isInService() and either passes through, returns 503 JSON for API calls, or serves the setup page via error.html with errPage="setup" - FlexServer._isInService(): returns true if GRIST_IN_SERVICE is set, if in test env (unless GRIST_FORCE_SETUP_GATE), or if real auth is configured - Boot page (/boot/:key) validates key and redirects to /admin - Gate uses req.originalUrl (not req.path) to correctly allow /v/ prefixed static assets after inspectTag middleware rewrites Client changes: - errorMain.ts: skip API calls (useApi: false) when errPage is "setup" - errorPages.ts: new createSetupPage() with three numbered steps, toggle between getgrist.com sign-in and boot key entry Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the private _isInService() method after all public methods in FlexServer to satisfy @typescript-eslint/member-ordering rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Make PageContents.leftPanel optional so pages can use pagePanels() without a side panel. Guard all left panel references with optional chaining and conditionals. The left pane DOM, resizer, and narrow- screen opener are only rendered when leftPanel is provided. The setup page now uses pagePanels() with no leftPanel — just a top header bar containing the language picker, and the main content area. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The setup page now lets operators configure "Sign in with getgrist.com" directly, replacing the old env-var-only instructions. The flow requires GRIST_ADMIN_EMAIL to be set first, then the operator registers on getgrist.com with that same email and pastes back a config key. The server verifies the key's owner email matches GRIST_ADMIN_EMAIL, proving the person at the keyboard is the declared admin. - POST /api/setup/configure-auth endpoint (only active when setup gate is shown, returns 404 once server is in service) - Setup page UI: registration link, config key textarea, error/success states - In-process TestServer sets GRIST_IN_SERVICE to bypass setup gate - New test suite: test/nbrowser/SetupConfigureAuth.ts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All test suites source test_env.sh, so this single line fixes every in-process and spawned test server that was hitting the setup gate (503 responses). Replaces the per-TestServer workaround from the previous commit. Tests that explicitly test the gate (SetupPage, SetupConfigureAuth) delete GRIST_IN_SERVICE in their before hooks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a sandbox-availability boot probe that tests each sandbox flavor (gvisor, pyodide, macSandboxExec) by actually creating and running a sandbox process. The setup page detects available flavors in parallel, displays them as selectable cards with status badges and descriptions, and saves the choice via a new configure-sandbox endpoint. Includes an "unsandboxed" fallback option clearly marked as not recommended. Step 2 and Step 3 (Backups) are greyed out until Step 1 is complete. Also includes throwaway mockup controls for reviewer convenience (documented in MOCKUP.md) and tests in SetupConfigureSandbox.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add "Open full admin panel" link in Step 2, inline with Configure button - Allow /api/session and /api/config through setup gate so admin panel loads without errors - Fix null crash in AuthenticationSection when currentValidUser is null during setup gate (use optional chaining instead of non-null assertion) - Show "No Sandbox" option once any sandbox is confirmed or all checks done - Animated pulse on "Checking..." badge for visual feedback - Grey out Backups section until step 1 complete - Use known GRIST_BOOT_KEY in tests instead of fetching from /api/install/prefs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reframe step 1 from "authentication" to proving the installer's identity: - Header: "Set up your Grist installation" - Step 1: "Verify you are the installer" with explanation - Replace toggle links with a segmented control picker for choosing between getgrist.com registration and boot key methods - Remove "Recommended" badge (no longer meaningful with new framing) - Update all test files to match new header text Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Step 3 of the setup page now detects external storage via a new `external-storage` boot probe and shows radio cards for each backend (MinIO, S3/AWS, Azure, No Storage). MinIO is always selectable with setup instructions shown when selected but not configured; S3 and Azure grey out as unavailable in grist-core. Includes API probe tests and browser tests for the interactive flow. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Step 4 lets the installer bring Grist into service from the setup page after completing sandboxing and storage configuration. A "Go Live" button calls POST /api/setup/go-live which persists GRIST_IN_SERVICE to the DB and sets process.env for immediate effect (no restart needed). Also adds POST /api/admin/maintenance to the admin panel so an admin can take Grist back out of service, showing the setup page again. The admin panel gets a "Maintenance" section with a confirmation modal. Other changes: - GRIST_IN_SERVICE added to ActivationsManager pick list for persistence - Sandbox probe timeout reduced from 10s to 2s during testing - Step 4 gated on steps 2/3 completion (sandbox configured + storage selected) - Manual skip hint: set GRIST_IN_SERVICE=true and restart Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace vertically stacked step sections with a tab bar at the top and show/hide content panels. All step content stays in the DOM so background probes keep running and test IDs remain findable. Auto-advance tabs on step completion (step 1 → 2, step 2 → 3). Each tab shows a checkmark when its step is complete. Tab 4 uses a rocket emoji. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Major changes: - Replace inline setup page with boot-key login redirect flow: the setup gate now redirects to /auth/boot-key for authentication, then to /admin/setup for the wizard - Secure boot-key two-step login: validate boot key via session flag instead of passing it in URL query params (prevents leaking in browser history, server logs, referrer headers) - Add authentication step to setup wizard (step 2), reusing the shared AuthenticationSection component from the admin panel - Add sandbox and storage configurator components to admin panel with shared probe logic and idempotency guards - Remove old setup page (createSetupPage), configure-auth endpoint, and getgrist.com registration flow from FlexServer - Make AuthenticationSection.controls optional for wizard context - Delete SetupConfigureAuth tests (endpoint removed), update SetupConfigureSandbox tests for 4-step wizard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…hed visuals - Replace flat tab bar with a horizontal progress rail: connected dots with animated fill bar tracking the active step, green checkmarks on confirmed steps - Implement checkmark theory: checkmarks mean "admin actively confirmed this step" not "something was auto-detected" — each step tracks a separate confirmed flag persisted via sessionStorage - Polish all setup components to match boot key login page aesthetic: 12px card radius, subtle shadows, atmospheric glow, staggered entrance animations, hover lift on selection cards, press-scale on buttons - Redesign boot key login to two-phase client-side flow: Check key → reveal email → Continue (no page reload between phases) - Move session initialization before setup gate in MergedServer - Update tests for new two-phase boot key login flow - Document visual design language and checkmark theory in PATHS.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…style issues Move public buildDom() before private methods in SetupWizard.ts to satisfy member-ordering rule. Convert require() to static import in FlexServer.ts. Apply auto-fixes for import ordering, arrow-parens, trailing commas, operator linebreak, and indentation across changed files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…p gate in tests Three fixes for CI test failures: 1. BootKeyLogin: getAdminProfile() crashed with .split() on undefined when GRIST_ADMIN_EMAIL wasn't set. Now returns undefined and addEndpoints skips user creation gracefully. 2. FlexServer: forcedLoginMiddleware used raw process.env.GRIST_IN_SERVICE as a truthy check, where the string "false" is truthy in JS. Simplified to only check GRIST_FORCE_LOGIN (matching main branch behavior). Removed unused _hasRealAuth method. 3. FlexServer: GRIST_IN_SERVICE from DB activation prefs was propagated to process.env, causing the setup gate to activate in test environments that create fresh databases. Now stored in _dbInService field instead, and _isInService() checks explicit env var first, then test socket, then DB value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tips panel was impossible to open once the user typed anything in the boot key field. Now tipsOpen overrides the collapsed state so "Need help?" always works. Added chevron indicators and a blue left border with background highlight so the expanded tips are visually obvious. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When GRIST_ADMIN_EMAIL was already set (e.g. from a previous server run), completeLogin() skipped updating it even when the user entered a different email. The session got the new email but the admin check still used the old one, causing 403 on all admin API calls (/api/probes, /api/config/auth-providers). Fix: always update GRIST_ADMIN_EMAIL when the submitted email differs. Add regression tests for this scenario and for GRIST_DEFAULT_EMAIL interaction. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bing - SandboxConfigurator: lead with recommended sandbox as a hero card, collapse alternatives behind "Other options..." toggle. User-focused descriptions instead of tech jargon. Per-flavor progress during probing. - Admin panel: full interactive configurator (not read-only), matching the principle that admin panel is the superset of wizard capabilities. - Wizard: defer auth checks to step 2, storage probes to step 3. Only sandbox probes on initial load (step 1 is immediately visible). - AdminChecks: add forceCheckResult() for mockup controls. - Wizard mockup panel: add auth provider simulation buttons. - Add REDESIGN.md distilling PATHS.md to principles, user paths, visual design, and key insights (~200 lines vs ~1000). - Update tests for new UI structure (alternatives toggle). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d8a4041 to
c9b3cb8
Compare
Replace the single-path boot key entry with three equal-weight tabs: - "Enter boot key" — shows ASCII banner and input field - "Set your boot key" — explains GRIST_BOOT_KEY env var - "Turn off this check" — explains GRIST_IN_SERVICE with warning Each tab gives complete, self-contained guidance so users unfamiliar with server logs can find an alternative path without hunting for buried help links. Cross-links between tabs for discoverability. Refined typography hierarchy (14px primary, 12.5px secondary, 12px uppercase labels) and consistent spacing via flex gap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace tab bar with segmented control (neutral active state so the submit button remains the primary CTA) - Fix dark mode: use theme tokens for track and input borders - Input field larger with subtle primary glow to draw attention - Consolidate guidance text, center ASCII banner - Remove redundant "Boot key" label (placeholder suffices) - Align email/button section padding with tab content - Refine typography hierarchy and spacing - Add cross-links between tabs for discoverability - Add Docker tip to "Turn off this check" tab - Lint fixes Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract permission defaults (team creation, personal sites, anonymous access, anonymous playground) into a shared PermissionsConfigurator used by both the setup wizard and admin panel. Includes profile presets (Locked down / Recommended / Open) via segmented control, unsaved changes indicator, single-org note, and dirty state tracking. - New POST /api/admin/save-permissions endpoint for admin panel - go-live endpoint accepts permissions in request body - Allowlist expanded for 4 permission env vars in ActivationsManager - GoLiveControl accepts getBody callback for extra request data - Admin panel Security section shows Default Permissions item - Setup wizard step 4 uses shared component instead of inline code - Auth tab "Skip for now" renamed to "Continue" - Sandbox configurator: skip checks button, unavailable options selectable - Boot-key login: segmented control, dark mode fixes, typography polish - REDESIGN.md: working notes on PR scope checking Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file was created as part of the setup wizard mockup controls but was never committed, causing TS2307 "Cannot find module" errors in CI for both SetupWizard.ts and errorPages.ts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
The maintenance endpoint had its own simplified check for GRIST_IN_SERVICE that returned false when the env var was unset, contradicting the actual server behavior (default true for upgrades). Expose FlexServer._isInService() as a public isInService() method on the GristServer interface so all callers use the same logic: env var → test env → DB-persisted → default true. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…led out Instead of waiting for all probes to finish, show the recommendation as soon as every higher-preference flavor has been resolved. If gvisor is still checking, don't jump to pyodide — wait until gvisor is known unavailable before recommending the next best option. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
Steps are now defined as a single array with string IDs (sandbox, auth, storage, apply). Progress rail positions, navigation, save/restore, and test selectors all derive from this array. Reordering, adding, or removing steps only requires editing the array. Updates test selectors from .test-setup-tab-2 to .test-setup-tab-auth etc. Adds testing section to REDESIGN.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… key input The setup wizard now has a Server step (first tab) with: - Base URL section with confirm/skip inline buttons - Edition selector (Full / Trial / Community Edition) as a segmented control - Activation key input when Full is selected and available - "I understand" checkbox when Full Grist is unavailable - Continue button gated on both sections being addressed - All state persisted to sessionStorage across reloads - Mockup panel controls for edition availability Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
- Sandbox button always says "Continue" instead of flipping between "Configure" and "Continue" during probing - GRIST_SINGLE_ORG warning now shows the actual org name when available - Boot key setup page generates a random key upfront with a Copy button, using the same _longCodeForExample() pattern from the admin panel Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Consolidate edition selector to two options (Full Grist / Community Edition), remove activation key input, add dynamic continue button text explaining what's needed, show "Confirmed" vs "Will do later in administrator panel" with edit icon for both URL and edition sections. Add building-from-source link for unavailable Full Grist and FAQ link for activation keys. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
Document capitalization convention in REDESIGN.md: sentence case everywhere except single-word buttons and brand-like actions (Go Live, Apply & Restart). Fix instances in wizard UI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
- Fix open redirect in boot-key login: validate `next` param is relative - Remove dead isDirty/updateDirty/urlDirty code from ServerConfigurator - Remove unused Notifier import and constructor param from ServerConfigurator - Fix body variable shadowing in GoLiveControl.goLive() - Fix stale ErrorInLoginMiddleware comment in BootKeyLogin - Remove unused &-setup CSS variant from errorPages - Remove stale _build/test/nbrowser/SetupConfigureAuth.js (source was deleted but compiled output remained, causing 13 spurious test failures) - Add REVIEW.md with PR summary and ranked code problems Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
…ledgment Replace the old AuthenticationSection-based wizard step with a new AuthConfigurator component that matches the sandbox step's visual language (hero card, filled badges, left accent stripe). Running without auth now requires an explicit checkbox acknowledgment, making the risk as visible as the "No Sandbox" warning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace AuthenticationSection (admin panel) and the old wizard-only AuthConfigurator with a single component used by both contexts. Key changes: - Hero card pattern with status-first display (green/amber/red/blue) - Suppress stale activeError after provider reconfiguration - getgrist.com Configure button available in both wizard and panel - Active provider excluded from alternatives list (no duplication) - "Other methods" header when hero shows an active provider - Session auth gate on /admin/setup — shows boot-key login link instead of a broken wizard when session expires - tabindex=-1 on setup page for text selection/copy support - hasRealAuth considers willBeActive providers (enables Continue button after configuring auth before restart) - Console warnings on API failures instead of silent swallowing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Deployed commit |
This is just a mockup, for trying ideas.
Motivation: Fresh Grist installations currently start wide open with no authentication — anyone who can reach the URL has full access. After surveying how 20 popular self-hosted apps handle initial auth (hardcoded creds, first-visitor-wins, auth-off-by-default, etc.), this adopts a "not in service" posture inspired by Frigate/Pi-hole's "random key to logs" pattern, combined with getgrist.com sign-in for operators who have an account (a bit like Plex).
DON'T TAKE THE CODE HERE SERIOUSLY
It will need reworking once the ideas are hammered out. Just here for the preview.