Where documents are stored
A SideDoc deployment never stores user data on the server. Documents live in the user's browser, and users can keep backups of their workspace in storage they control: their Google Drive, an S3-compatible bucket, or a folder on their own computer.
The working copy: the browser
The standalone editor (the image and sidedoc.app) keeps the workspace in the browser's IndexedDB, on the user's own device. Every edit is saved there. As an operator you have nothing to configure, back up or protect for this, but tell your users:
- The workspace is tied to one browser profile on one device, and to the site's origin. Moving SideDoc to another domain starts users with an empty workspace.
- Clearing site data, or a private window, removes the workspace. Users keep copies with Export or with backups.
Backups
The Settings window (Settings… in the toolbar) has a section Back up your documents to. A user picks a place, connects, and then:
- Back up now writes the whole workspace (all documents, the profiles and type library they use, and a timestamp) as a new JSON file. Manual backups are never deleted.
- While connected, the editor writes automatic backups: every 5 minutes and once a day, only when the workspace changed. It keeps the newest 20 automatic and 14 daily backups of each workspace and deletes older ones.
- Restore backup… lists the backups, newest first, and after a confirmation replaces the browser's whole workspace with the chosen one.
- When a user switches from one place to another, the editor offers to copy all backups across.
There is no live sync, no merge and no conflict detection. The browser talks to the storage directly; no SideDoc server is involved. The user guide describes this from the user's side: Settings and backups.
Each backup is a new file named <prefix><workspace>--<kind>--<UTC time>.json, for example sidedoc-workspace-<workspace>--auto--20261028T101500Z.json, where kind is manual, auto or daily. Google Drive and S3 use the prefix sidedoc-workspace-; the local folder uses no prefix and the suffix .datasheet.json.
Which places are available
| Place | Default | What it needs |
|---|---|---|
| Local folder (Chromium browsers) | on | Nothing. Uses the browser's File System Access API, so it works in Chrome, Edge and other Chromium browsers only. Switch off with VITE_ENABLE_LOCAL_FS_PERSISTENCE=false at build time. |
| Google Drive | off | A Google OAuth client and a CSP that allows Google. See Google Drive. |
| S3-compatible storage | off | Cannot be switched on in the current version. See S3-compatible storage. |
All three are always listed in the Settings window. A place that is switched off shows Not connected with a message such as "Google Drive persistence is disabled.", and cannot connect. These settings are build-time only: run-time configuration (config.js, the container environment) does not cover them.
Google Drive
Backups go to the Drive app data folder (OAuth scope drive.appdata): a hidden folder that only SideDoc can see, so the files do not show up in the user's Drive and SideDoc cannot read the user's other files.
- In the Google Cloud console, create an OAuth client of type Web application and add the SideDoc origin (for example
https://sidedoc.example.org) under Authorised JavaScript origins. Enable the Google Drive API for the project. - Build with
VITE_ENABLE_GOOGLE_DRIVE_PERSISTENCE=trueandVITE_GOOGLE_OAUTH_CLIENT_ID=<client id>. A client id is not a secret. - Serve a CSP that allows Google's sign-in and the Drive API:
script-src https://accounts.google.com,frame-src https://accounts.google.com, andconnect-src https://www.googleapis.com https://oauth2.googleapis.com.
Drive needs your own CSP
The CSP that a default build embeds, and the one the container image sends, do not allow these Google hosts, and the container can only extend connect-src (SIDEDOC_CSP_CONNECT_SRC), not script-src or frame-src. Drive therefore only works with static hosting built with SIDEDOC_CSP_META=false behind a web server that sends a CSP with the sources above.
S3-compatible storage
S3 cannot be switched on yet
S3 storage is off by default and is switched on at build time with SIDEDOC_ENABLE_S3_PERSISTENCE=true. The frontend passes import.meta.env to the backend, and Vite only puts VITE_* variables there. The S3 settings have no VITE_* alias, so in the current version no build can switch S3 on: the Settings window shows "S3-compatible persistence is disabled." and Connect S3 Bucket fails. The rest of this section describes what a bucket needs once this is fixed.
Settings
The variables are listed under S3 storage in the configuration reference. Leave endpoint, bucket and keys empty so that each user enters their own. Anything set at build time is in the public bundle, so never configure S3 keys in a build for a hosted deployment.
The editor remembers the endpoint, bucket, region and access key ID in the browser's localStorage, never the secret. Where the browser supports passkeys with the PRF extension, the secret is encrypted with a key derived from a passkey and stored encrypted; the user unlocks it with the passkey once per tab. Otherwise the secret is kept in memory and is gone after a reload. Passkeys need the site to run on a domain name (or localhost), not an IP address.
Bucket setup
Any S3-compatible service works: AWS S3, Cloudflare R2, MinIO, Scaleway, OVHcloud and others. Requests are signed in the browser with AWS Signature V4.
Create a bucket. Keep it private: no public access, no website hosting.
Create an access key restricted to that bucket and, ideally, to the
sidedoc-workspace-prefix. SideDoc usesListObjectsV2(GET ?list-type=2, also on connect, to check the keys),GetObject,PutObject, andDeleteObjectto remove automatic and daily backups beyond the retention limits. An AWS-style policy:json{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::sidedoc-workspaces", "Condition": { "StringLike": { "s3:prefix": "sidedoc-workspace-*" } } }, { "Effect": "Allow", "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"], "Resource": "arn:aws:s3:::sidedoc-workspaces/sidedoc-workspace-*" } ] }Without
DeleteObject, backups still work but old automatic and daily backups pile up. Everyone who uses the same bucket and prefix sees each other's backups in the restore list. Give each user their own bucket, or their own key restricted to their own objects.Allow cross-origin requests from the SideDoc origin. Without CORS the browser blocks every request:
json[ { "AllowedOrigins": ["https://sidedoc.example.org"], "AllowedMethods": ["GET", "PUT", "DELETE"], "AllowedHeaders": ["authorization", "content-type", "x-amz-date", "x-amz-content-sha256"], "ExposeHeaders": ["ETag"], "MaxAgeSeconds": 3600 } ]Apply it with
aws s3api put-bucket-cors --bucket sidedoc-workspaces --cors-configuration '{"CORSRules": [...]}', or in the provider's console. MinIO and some others configure CORS per server rather than per bucket.Allow the endpoint in the CSP's
connect-src. For the container:shSIDEDOC_CSP_CONNECT_SRC="https://s3.eu-central-1.amazonaws.com"For static hosting, add it to the CSP header your server sends, and build with
SIDEDOC_CSP_META=false: the<meta>policy that a default build embeds allows only the site itself and Sentry, and would block the bucket even if the header allows it. See Static hosting.Path-style URLs (
https://<endpoint>/<bucket>/<key>) need only the endpoint host. Virtual-host URLs connect tohttps://<bucket>.<endpoint host>; allow that host instead. The Use path-style addressing box in the Settings window andSIDEDOC_S3_FORCE_PATH_STYLEchoose between the two.