## Summary This PR adds full desktop (Tauri) support for the shared signing feature when connected to a self-hosted server, and fixes several bugs discovered during that work. ### Feature gating Shared signing, file sharing, and share links are proprietary server features that require an authenticated self-hosted session. Previously these were read directly from `config` with no awareness of connection mode or auth state, meaning the UI could appear in SaaS/local mode or when logged out. - Introduce `useGroupSigningEnabled` and `useSharingEnabled` hooks with core implementations (web behaviour unchanged) and desktop overrides that require `selfhosted` mode + an active authenticated session - Extract shared subscription logic into `useSelfHostedAuth` (connection mode + auth state + config refetch) - `QuickAccessBar` now derives all three flags from the hooks instead of raw config ### Config timing fix When a user logs in via the SetupWizard, the `jwt-available` event fires a config fetch *before* the mode is switched to `selfhosted`. This meant the config was fetched from the local bundled backend (port ~59567) which has no knowledge of `storageGroupSigningEnabled`, causing the group signing button to stay hidden until a full page refresh. `useSelfHostedAuth` detects the mode transition and triggers a fresh config fetch at the correct moment, after the self-hosted URL is active. ### Bug fixes **`SignPopout.tsx`** — Manually setting `Content-Type: multipart/form-data` on two `FormData` POST requests stripped the auto-generated boundary, causing a `400 bad multipart` from the server. Removed the explicit headers so Axios sets them correctly. **`tauriHttpClient.ts`** — `response.json()` was called before `response.ok` was checked. A plain-text error body from the server (e.g. `"Cannot sign..."`) caused a `SyntaxError` that fell into the network error catch block and was reported as `ERR_NETWORK`, hiding the real failure. The fix checks `response.ok` first, reads error bodies as text, and handles empty 200 bodies (returning `null` instead of throwing). --- ## Testing ### Prerequisites - Desktop app running in self-hosted mode pointed at a local Stirling-PDF instance (`http://localhost:8080`) - The self-hosted instance has group signing and storage enabled in settings - At least two user accounts on the self-hosted instance ### 1. Feature gating — group signing button | Step | Expected | |---|---| | Open the desktop app in **local mode** (no server configured) | Group signing button absent from QuickAccessBar | | Switch to self-hosted mode but **do not log in** | Group signing button absent | | Log in to the self-hosted server | Group signing button appears without requiring a page refresh | | Log out | Group signing button disappears immediately | | Log back in | Group signing button reappears without a page refresh | ### 2. Feature gating — file sharing Repeat the same steps above, verifying the share and share-link buttons in the file manager follow the same visibility rules. ### 3. Create a signing session 1. Log in, open the group signing panel from QuickAccessBar 2. Select a PDF, add a participant, configure signature defaults and submit 3. Verify the session is created successfully (no `400 bad multipart` error) ### 4. Participant signing 1. As the invited participant, open the signing request from QuickAccessBar 2. Upload or draw a signature and submit 3. Verify signing completes successfully (no `ERR_NETWORK` error) ### 5. Error surfacing 1. Attempt an action that the server rejects (e.g. sign a document with an invalid certificate) 2. Verify the actual server error message is shown rather than a generic network error
Frontend
Environment Variables
The frontend requires environment variables to be set before running. npm run dev will create a .env file for you automatically on first run using the defaults from config/.env.example - for most development work this is all you need.
If you need to configure specific services (Google Drive, Supabase, Stripe, PostHog), edit your local .env file. The values in config/.env.example show what each variable does and provides sensible defaults where applicable.
For desktop (Tauri) development, npm run tauri-dev will additionally create a .env.desktop file from config/.env.desktop.example.
Docker Setup
For Docker deployments and configuration, see the Docker README.
Available Scripts
In the project directory, you can run:
npm start
Runs the app in the development mode.
Open http://localhost:3000 to view it in your browser.
The page will reload when you make changes.
You may also see any lint errors in the console.
npm test
Launches the test runner in the interactive watch mode.
See the section about running tests for more information.
npm run build
Builds the app for production to the build folder.
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.
Your app is ready to be deployed!
See the section about deployment for more information.
npm run eject
Note: this is a one-way operation. Once you eject, you can't go back!
If you aren't satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
Learn More
You can learn more in the Create React App documentation.
To learn React, check out the React documentation.
Code Splitting
This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting
Analyzing the Bundle Size
This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size
Making a Progressive Web App
This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app
Advanced Configuration
This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration
Deployment
This section has moved here: https://facebook.github.io/create-react-app/docs/deployment
npm run build fails to minify
This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify
Tauri
In order to run Tauri, you first have to build the Java backend for Tauri to use.
macOS/Linux:
From the root of the repo, run:
./gradlew clean build
./scripts/build-tauri-jlink.sh
Windows
From the root of the repo, run:
gradlew clean build
scripts\build-tauri-jlink.bat
Testing the Bundled Runtime
Before building the full Tauri app, you can test the bundled runtime:
macOS/Linux:
./frontend/src-tauri/runtime/launch-stirling.sh
Windows:
frontend\src-tauri\runtime\launch-stirling.bat
This will start Stirling-PDF using the bundled JRE, accessible at http://localhost:8080
Dev
To run Tauri in development. Use the command in the frontend folder:
npm run tauri-dev
This will run the gradle runboot command and the tauri dev command concurrently, starting the app once both are stable.
Note
Desktop builds require additional environment variables. See Environment Variables above -
npm run tauri-devwill set these up automatically fromconfig/.env.desktop.exampleon first run.
Build
To build a deployment of the Tauri app. Use this command in the frontend folder:
npm run tauri-build
This will bundle the backend and frontend into one executable for each target. Targets can be set within the tauri.conf.json file.
Note
Desktop builds require additional environment variables. See Environment Variables above -
npm run tauri-buildwill set these up automatically fromconfig/.env.desktop.exampleon first run.