Skip to content

Repository files navigation

Sezi Server

The ready-made package for running your own server for Sezi — end-to-end encrypted, outward-closed group messaging and application platform. The server is a blind relay: it cannot read message content, media or plugin data. It queues and moves encrypted bytes, nothing more. The keys stay on the devices.

Deploy to Cloudflare

Install (five minutes, no command line)

  1. Create a Cloudflare account (free): https://dash.cloudflare.com/sign-up
  2. Press Deploy to Cloudflare above. Cloudflare copies this repository into your own GitHub account, creates the database (D1) and the rate-limit store (KV, the brute-force brake) for you, and deploys.
  3. When the deploy finishes you get an address: https://sezi-server.<your-account>.workers.dev
  4. In the Sezi app, under "Add a server → Set up your own", paste that address. The first account to register becomes the server's owner — invitations, limits, everything, managed from the app.

That is all. The signing keys are generated by the server on its first boot; the database schema installs itself on the first request. No wrangler secret, no migration command. No card details.

Lite install: this template installs "light" — messaging, groups and calls work in full; for photo and file sharing you enable R2 storage in Cloudflare afterwards (it asks for card verification; free up to 10 GB, a small fee beyond). How: enable R2 in the dashboard → create a bucket named sezi-media → Worker → Settings → Bindings → add R2 (MEDIA / sezi-media). No code, no commands; the app shows the feature by itself. Running it on your own machine with wrangler dev? Then no R2 subscription is needed at all: uncomment the [[r2_buckets]] block in wrangler.toml and media is stored on the local disk.

What you get

  • 🔒 A blind relay — content is E2E encrypted; the server sees only sizes and counts
  • 👑 Owner administration from the app — invitations, members, retention, storage limits, the usage panel
  • 📊 Usage panel — storage / calls (TURN) / daily and monthly volume; optionally a "matches your bill exactly" mode with a CF API token (which you also enter from the app)
  • 💸 Budget guards — a monthly TURN ceiling plus owner-set storage quotas: no surprise invoice on the free tier
  • 📱 Multi-device, groups (Megolm), voice and video calls (LAN/STUN; CF TURN if you want it), a plugin platform
  • 🧲 Pluggable storage — keep media in YOUR OWN store instead of, or alongside, R2 (see below)

🧲 Pluggable storage: your own cloud, or your own disk

The media store is not limited to R2. You can attach any S3-compatible store to the server from the app — no code, no commands, no redeploy:

  • Cloud: Backblaze B2, Wasabi, iDrive e2, MinIO in the cloud, another R2 account…
  • Your own disk (self-host): a MinIO running on the same machine (e.g. a Raspberry Pi with a big HDD) → http://localhost:9000

How: in the app, as owner → Server → panel → the STORAGE section → add a store (endpoint / region / bucket / access keys) → Test. That is it; new media is written there from then on. The same place drains a store (blobs are moved off it) and removes it; health and fullness are shown there too.

A note on privacy: media blobs are written end-to-end encrypted — whichever provider you attach (Backblaze, MinIO, anyone) sees only encrypted rubbish. The keys never leave the devices.

Alternative: install from the command line

git clone https://github.com/hasantr/sezi-server && cd sezi-server
npx wrangler login
npx wrangler d1 create sezi          # put the database_id from the output into wrangler.toml
npx wrangler kv namespace create RATE_LIMIT   # put the id from the output into wrangler.toml
npx wrangler deploy

The build/ directory contains compiled WASM — you do not need to install Rust. To build it yourself from source: cargo install worker-build && worker-build --release.

Running it on your own machine or VPS (without Cloudflare, advanced)

You can run the server around the clock on your own VPS or device (Raspberry Pi / mini-PC) without touching Cloudflare at all. This keeps wrangler dev's local workerd alive under systemd and exposes it either through a cloudflared tunnel or a caddy TLS reverse proxy.

⚠️ Security-critical: in a self-managed install the relay must listen on 127.0.0.1 only; outside access must come solely through the tunnel or the TLS proxy. --ip 0.0.0.0 puts the server on the internet as plain HTTP — do not.

A ready hardening kit (an EXACT wrangler pin, localhost binding, a WS shim, atomic backup, a healthz watchdog, ufw) is walked through step by step in deploy/ → deploy/README.md. Self-hosting uses its own config (wrangler.selfhost.toml, with the rate-limit KV enabled); the "Deploy to Cloudflare" button is unaffected by it.

Optional configuration

What How What happens without it
Your own signing key wrangler secret put JWT_SIGNING_KEY (Ed25519 PKCS8 PEM) Generated automatically on first boot (stored in D1)
TURN relay for voice and video CF Realtime → TURN key → TURN_KEY_ID + TURN_API_TOKEN secrets Calls work over LAN/STUN (enough on most networks)
Android push A Firebase project → FCM_PROJECT_ID var + FCM_SERVICE_ACCOUNT secret Delivered while the app is open (WS); it waits while closed
Usage report matching the bill From the app: Server → Usage → CF Analytics → enter a token The server reports its own counters

Updating

  1. On your fork in GitHub, use Sync fork → Update branch.
  2. On a Cloudflare-connected install this usually triggers a redeploy on its own. If it does not, trigger it by hand from the Cloudflare dashboard → Workers & Pages → your server → the Deployments/Builds tab → "Retry/Deploy"; or with one command on your computer, inside the fork's directory: npx wrangler deploy.

The database schema updates itself (self-migration) — no data is lost.

More than one server in the same account

Watch out for a database name collision. When you install a second server into the same Cloudflare account, make the D1 database name unique on the setup screen (e.g. sezi-family, sezi-school). If you reuse the same name the two servers share one database and their members and data mix together. Different Worker and KV names are harmless. Note that free-plan quotas (requests, storage) are shared across every server in the account.

Removing an installation

Deleting the Worker does not delete the database. D1 is an account-level resource that the Worker merely binds, so deploying again under the same name hands the new Worker the old database — the previous owner, members and signing key all come back. A clean start needs the database gone too. In full, what a deploy creates:

Name What it is
sezi-server The Worker. Deleting it stops everything and takes the user inboxes with it.
sezi The D1 database. The one people forget.
RATE_LIMIT The KV namespace. Harmless to leave.
sezi-media The R2 bucket, only if you enabled media. Cloudflare will not delete it until it is empty.

The GitHub repository the deploy created in your account stays until you remove it there.

⚠️ Do not simply abandon a server. While nobody has registered on it, the first person who learns its address becomes its owner — that is how the very first account is created, and it stays open until somebody takes it.

Architecture (briefly)

Rust → WASM Cloudflare Worker. D1 (SQLite) for metadata and the queue, R2 for encrypted media, a per-user Durable Object inbox (WebSocket). Content is end-to-end encrypted with vodozemac (Olm/Megolm) — the server cannot read it, by design.

Licence

Not decided yet — to be added before release.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages