Self-host on Cloudflare
Deploy Seal on your own Cloudflare account with D1, R2, and workers.dev
Self-host on Cloudflare
Run Seal on your Cloudflare account. Signing loop works with Workers + D1 + R2. No Vortex resource IDs. No Docker compose.
Deploy to Cloudflare
Seal is a pnpm monorepo. On the Workers Builds setup screen:
| Field | Value |
|---|---|
| Root directory | / |
| Build command | pnpm install |
| Deploy command | pnpm selfhost |
CLI (recommended)
Requires Node 24+, pnpm 12+, and a Cloudflare account.
git clone https://github.com/VortexNYC/seal.git
cd seal
pnpm install
pnpm exec wrangler login
pnpm selfhost
Non-interactive secrets:
BETTER_AUTH_SECRET=$(openssl rand -base64 32) \
TOKEN_HASH_SECRET=$(openssl rand -base64 32) \
INTERNAL_API_KEY=$(openssl rand -base64 32) \
pnpm selfhost
What the script does:
- Prompts for (or reads) the three secrets above
- Deploys
apps/api[env.selfhost]— wrangler auto-provisions D1 + R2 - Applies D1 migrations by binding name (
D1) - Sets
BETTER_AUTH_URL/APP_URL/ALLOWED_ORIGINSto your*.workers.devURLs - Builds and deploys
apps/webagainst that API
You get something like:
Web: https://seal-web.<account>.workers.dev
API: https://seal-api.<account>.workers.dev
Cold-account checklist (stranger-shaped)
Treat a new Cloudflare login as the real proof. Vortex-account dogfood is
safe (seal-selfhost-* names) but does not prove stranger readiness.
Do these in order; stop when one fails.
- Clone + install on a clean machine (no prior
wranglerlogin, no copied Vortex.dev.vars):git clone https://github.com/VortexNYC/seal.git && cd seal pnpm install pnpm exec wrangler login # *your* account pnpm selfhost - Open the printed web URL → sign up → verify email → create a workspace
- Developer → API Keys → create
seal_… - Golden path (CLI + public signing half → completed → audit):
Alternate raw HTTP smoke (no CLI):SEAL_API_KEY=seal_… pnpm run prove:golden-path \ --api https://seal-selfhost-api.<account>.workers.dev \ --app https://seal-selfhost-web.<account>.workers.devSEAL_API_KEY=seal_… node scripts/smoke-prod.mjs \ --api https://seal-selfhost-api.<account>.workers.dev - Sign once in a real browser from the
signing_url(or the UI) so you trust the SPA route, not only the public API harness - Email — only after the loop above is green (see next section)
If step 1–4 fail on a cold account, that is a product bug — fix docs/script before claiming self-host works.
Email (required for real invites)
selfhost ships with a Cloudflare Email Sending binding (EMAIL). Signing
links still work from the UI/API without mail; invites need a verified sender
domain on your Cloudflare account.
- Enable sending for your domain:
pnpm exec wrangler email sending enable your-domain.com - Set
allowed_sender_addresses/EMAIL_FROMunder[env.selfhost]inapps/api/wrangler.tomlto an address on that domain - Redeploy:
pnpm --filter @seal/api exec wrangler deploy --env selfhost
Vortex dogfood uses noreply@seal.nyc (domain already enabled on the account).
Optional workers
| Worker | Needed for |
|---|---|
| API + web | Core upload → send → sign |
| anydoc | Rich PDF field detection |
| convert | DOCX → PDF (Containers) |
| mcp | Agent MCP surface on your origin |
PDF upload and signature collection work without anydoc/convert.
Custom domains
Start on *.workers.dev. When you own DNS, add routes under [env.selfhost]
(or promote to a dedicated production env with your own D1/R2 IDs — never
reuse Vortex’s).
Local development
For laptop-only work (no deploy):
cp apps/api/.dev.vars.example apps/api/.dev.vars
cp apps/web/.env.example apps/web/.env.local
pnpm run dev
See the repo CONTRIBUTING.md.
Safety
Selfhost worker / D1 / R2 names are seal-selfhost-* so they cannot overwrite
hosted production (seal-api, seal-web, seal-global, seal-documents). You
can dogfood on the Vortex account; for a customer-shaped cold proof, use a
separate Cloudflare login.