Skip to content

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 ​

PlaceDefaultWhat it needs
Local folder (Chromium browsers)onNothing. 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 DriveoffA Google OAuth client and a CSP that allows Google. See Google Drive.
S3-compatible storageoffCannot 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.

  1. 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.
  2. Build with VITE_ENABLE_GOOGLE_DRIVE_PERSISTENCE=true and VITE_GOOGLE_OAUTH_CLIENT_ID=<client id>. A client id is not a secret.
  3. 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, and connect-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.

  1. Create a bucket. Keep it private: no public access, no website hosting.

  2. Create an access key restricted to that bucket and, ideally, to the sidedoc-workspace- prefix. SideDoc uses ListObjectsV2 (GET ?list-type=2, also on connect, to check the keys), GetObject, PutObject, and DeleteObject to 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.

  3. 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.

  4. Allow the endpoint in the CSP's connect-src. For the container:

    sh
    SIDEDOC_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 to https://<bucket>.<endpoint host>; allow that host instead. The Use path-style addressing box in the Settings window and SIDEDOC_S3_FORCE_PATH_STYLE choose between the two.