# Sync Server Deployment

This guide is for people who want to run a Cosimo sync server. If you only want
to use an existing server from Cosimo, read the `Deck Sync` section of the user
guide instead.

The sync server stores and serves deck snapshots. It does not run scheduling
logic and does not merge independently edited decks. Owner uploads can include
review history; public and unlisted downloads are derived as content-only
bundles so another user's review database is not served accidentally.

## Basic Requirements

A deployed sync server should have:

- an HTTPS URL reachable by Cosimo clients
- a private storage directory that is not served as static web content
- regular server-side backups of that storage directory
- enough disk space for deck bundles, especially decks with audio
- a test pass covering login, upload, download, remote deck listing, and
  owner-only deletion before relying on it with real decks

The storage directory contains the SQLite sync database, OAuth application
credentials, bearer-token verifiers, pending login records, deck metadata, and
stored bundle files. Treat it as private application data.

## CGI Deployment

CGI behind an existing HTTPS web server is the preferred deployment model at
present. It is simpler to host, does not need a long-running process, and has
had the most direct deployment testing.

The intended layout is similar to Fossil's CGI mode. Put a small executable
config script where the web server runs CGI programs, but keep the real
`cosimo-sync-cgi` binary and the storage directory outside the web-readable
tree.

In that intended layout, neither the binary nor the stored deck data is served
as static web content. The web server only exposes the small CGI config script,
and that script is executed as CGI rather than offered as a download.

A minimal config script looks like this:

```text
#!/home/user/bin/cosimo-sync-cgi
storage-root: /home/user/private/cosimo-sync-storage
```

Mark the script executable and configure the web server so it runs as CGI. The
URL of that script is the sync server URL users enter in `Tools -> Options`.
For example, if the script is reachable as:

```text
https://example.invalid/cgi-bin/cosimo-sync.cgi
```

then Cosimo should use that exact URL. Cosimo appends `/api/v1/...` paths below
the same CGI endpoint.

Opening the CGI URL in a browser should show a small Cosimo Sync Server status
page. This is only a sanity check; Cosimo itself uses the API paths.

Owner routes need this request header to reach the CGI process:

```text
Authorization: Bearer ...
```

In CGI environments this is normally exposed as `HTTP_AUTHORIZATION`. If the
web server strips it, login may succeed but owner listing, upload, download, or
deletion will fail with an authentication error. On Apache-style setups this
may require `CGIPassAuth On` or the host's documented equivalent.

The CGI executable resolves storage in this order:

1. `--storage-root PATH`
2. `storage-root:` in the config script
3. `COSIMO_SYNC_STORAGE_ROOT`
4. a `cosimo-sync-storage` directory beside the CGI executable

Prefer an explicit private storage root outside the web-readable tree. In the
recommended layout above this keeps all sync data out of the document root. The
fallback directory beside the executable is convenient for testing, but may be
unsafe if you copy the real CGI binary into a web-readable CGI directory and
also let the web server serve sibling files as static content.

## Reverse-Proxy Deployment

The standalone `cosimo-sync-server` can also run behind a reverse proxy. This
is supported, but it is more operationally involved and currently less
exercised than CGI. It is most suitable when you control a VPS, dedicated
server, home server, or VPN host and can run a supervised process.

Run the server bound to a private address, for example:

```bash
cosimo-sync-server --bind 127.0.0.1:44791 --storage-root /home/user/private/cosimo-sync-storage
```

Put an HTTPS reverse proxy in front of it. The reverse proxy should provide the
public URL and TLS; the sync server itself has no built-in TLS and uses a small
HTTP implementation intended for this controlled backend role.

The standalone server handles one request per TCP connection and expects
`Content-Length` request bodies. A normal reverse proxy should buffer and pass
requests in a compatible form.

For OAuth rate limiting, the standalone server normally uses the TCP peer
address. Behind a same-host reverse proxy this is usually `127.0.0.1`, which
means all users share the same client-address limit. If the proxy supplies a
real client-IP header, start the server with:

```bash
cosimo-sync-server --bind 127.0.0.1:44791 --storage-root /home/user/private/cosimo-sync-storage --trusted-client-ip-header X-Forwarded-For
```

Only use this when the reverse proxy strips any incoming copy of that header
and then supplies its own trusted value. Cosimo only honours the configured
trusted header from loopback peers, so accidentally exposing the backend port
does not let remote clients spoof their rate-limit identity through this option.

## Limits

Both CGI and reverse-proxy deployments support the same limit environment
variables:

```text
COSIMO_SYNC_MAX_BUNDLE_BYTES
COSIMO_SYNC_MAX_BUNDLE_EXPANDED_BYTES
COSIMO_SYNC_MAX_BUNDLE_ENTRIES
COSIMO_SYNC_MAX_DECK_FILE_BYTES
COSIMO_SYNC_MAX_DECKS_PER_OWNER
COSIMO_SYNC_MAX_STORAGE_BYTES_PER_OWNER
COSIMO_SYNC_MAX_STORAGE_BYTES_TOTAL
COSIMO_SYNC_MAX_OAUTH_STARTS_PER_CLIENT_PER_HOUR
COSIMO_SYNC_MAX_OAUTH_STARTS_PER_INSTANCE_PER_HOUR
COSIMO_SYNC_MAX_PENDING_OAUTH_LOGINS_PER_INSTANCE
```

CGI config scripts can also set the same limits with lower-case hyphenated
keys, such as:

```text
max-bundle-bytes: 52428800
max-bundle-expanded-bytes: 52428800
max-decks-per-owner: 20
max-storage-bytes-per-owner: 104857600
max-storage-bytes-total: 1073741824
```

Uploads are checked before they become authoritative. The server verifies
declared hashes, rejects unsupported bundle paths, rejects review databases in
content-only bundles, limits deck file size, and enforces configured storage
and deck-count quotas.

## OAuth Behaviour

Cosimo uses Mastodon-compatible out-of-band OAuth. The server registers or
reuses an OAuth application on the user's chosen instance, asks only for the
`read:accounts` scope, verifies the account, revokes the temporary Fediverse
token, and then issues a Cosimo bearer token.

The server stores OAuth application credentials per Fediverse instance so it
does not repeatedly create applications on the same instance. It stores Cosimo
bearer sessions as hashed verifiers rather than plaintext bearer tokens.

The registered OAuth redirect URI is:

```text
urn:ietf:wg:oauth:2.0:oob
```

It is not tied to the sync server domain. This makes moving a server simpler
than a callback-based OAuth design.

## Moving a Sync Server

To move a sync server to another host or domain, stop traffic and copy the
whole storage directory consistently. The SQLite database and bundle files must
match.

After copying:

1. Configure the new CGI script or reverse proxy to use the copied storage
   directory.
2. Open the new server URL in a browser and check that the status page appears.
3. Configure a Cosimo client to use the new sync server URL.
4. Log in again and test sync status, upload, download, remote deck listing,
   and deletion with a test deck.

Existing OAuth application registrations should normally continue to work
because the registered redirect URI is OOB and the application website points
to the Cosimo project rather than the deployment domain. If a Fediverse
instance rejects cached application credentials after a move, remove the cached
application row for that instance from the sync database so the server can
register a fresh application on the next login.

Changing the sync URL in Cosimo clears the local sync login state. Existing
server-side sessions may still be present in the copied database, but clients
should log in again after the move.

## Operational Checks

Before trusting a deployment with real decks:

1. Open the server root URL in a browser and confirm it shows the Cosimo Sync
   Server status page.
2. Configure Cosimo with the server URL.
3. Log in with a Mastodon-compatible account.
4. Upload a small test deck with study history.
5. Check `Deck -> Sync -> Sync Status`.
6. Open `Deck -> Sync -> Remote Decks` and inspect the uploaded deck details.
7. Download the deck to a separate local path or test installation.
8. Delete the remote test deck while logged in as its owner.
9. Confirm another account cannot delete a deck it does not own.
10. Confirm your server-side backup process includes the full sync storage
    directory.

Keep the storage directory private and backed up. The server is designed to
avoid serving review databases through public or unlisted downloads, but owner
study-history bundles still contain personal learning data.
