Deployment modes

One Docker image runs as SaaS or on-prem — pick your mode

TheSpider ships as one image, two modes. The same server image runs as the multi-tenant SaaS (DEPLOYMENT_MODE=saas) or as a single-tenant on-prem install (DEPLOYMENT_MODE=onprem). The image serves the /v1 API, the Better-Auth endpoints, and the web SPA from one origin, and runs Drizzle migrations on boot.

Running our own multi-tenant instance is the on-prem artifact's continuous integration test — the modes cannot drift apart because there is only one artifact.

Compare the modes

SaaS (multi-tenant)On-prem (single-tenant)
Serverthespider.xyz, run by uscustomer-run Docker Compose (bundled Postgres or BYO)
Orgsmanyexactly one, created at first boot (same code path, same RLS)
Entitlementssubscriptions + plan_limits (Stripe)claims in the license JWT
Object storageour S3/R2customer-supplied S3-compatible creds (MinIO works)
AI dedup keyplatform keycustomer-supplied key/endpoint (env)
Web authBetter Auth (OAuth + OIDC SSO)Better Auth (email/password + OIDC SSO)
BillingStripe metering on usage_eventsflat license; caps enforced from JWT claims
Client configserver = "https://thespider.xyz"server = "https://spider.corp.example"

Pick the entitlement source

Everything that gates features or quotas calls a single entitlements resolver interface with two implementations:

  • Subscription-backed (SaaS) — reads Stripe.
  • License-backed (on-prem) — reads JWT claims.

Nothing outside the SaaS billing module imports Stripe, and the core boots with no Stripe / OAuth / webhook modules configured.

Understand licensing (on-prem)

A license is a signed JWT (Ed25519) minted by mothership-side tooling (mothership/mint-license.tsnever in the image):

{ "sub": "customer", "plan": "...", "seats": 10, "max_projects": 5,
  "ai_dedup": true, "features": [], "iat": 0, "exp": 0, "grace_days": 14 }

Validation rules:

  • The server validates at boot and daily, against public keys embedded in the image, optionally refreshed from https://api.thespider.io/.well-known/jwks.json. Embedded keys make validation air-gap-safe; the JWKS URL exists for rotation.
  • Missing license → the instance boots read-only (reads/exports only).
  • Valid license → caps come from JWT claims. Exceeding max_projects returns 403 max_projects_exceeded; ai_dedup:false keeps dedupe heuristics-only.
  • Past exp + grace_days → the instance goes read-only: claims, results/imports, campaign-creation, config and triage writes return 403 read_only_license, while all reads and exports keep serving. We don't hold your data hostage.
  • A boot with a malformed/bad-signature license fails loudly instead of degrading.

Keep methodology current

On-prem instances pull methodology/config updates from the mothership using the license JWT as auth (GET /v1/method-updates), and every image release bundles a snapshot for air-gapped installs. An active license is what keeps the methodology current — the commercial lever that survives shipping the brain into customer infrastructure.

Choose your deploy target

Next steps