Providers

Selva doesn’t own a database, an auth service, or a file store. Those are providers: backends you pick at deploy time. The app is written against interfaces, so the same Selva runs on files on disk, on Supabase, or behind corporate SSO. You switch by config, not by forking.

Where credentials live follows from that choice. Point Selva at Supabase or corporate SSO and identity stays there, with Selva keeping only session tokens and authorization data. Run the local provider and Selva is the auth provider, storing emails and password hashes itself. Either way Selva also stores display names, invite emails, audit-event payloads, and solve telemetry: the deployment operator is the data controller for all of it.

Three roles

Mix implementations freely: Supabase for auth and data, files elsewhere, for example.

RoleInterfaceOwns
AuthIAuthProviderTokens, users, sessions. The identity it produces drives everything else.
DataIDataProviderMetadata: orgs, projects, definitions + version history, share links, invites, compute config, profiles, permissions.
StorageIStorageProviderBlob storage for definition files and assets.

These are plain TypeScript interfaces in @selvajs/platform. Each adapter lives in its own package.

What ships

ProviderBackendBest for
localFiles (JSON) + HMAC sessionsEval, single-instance
supabaseSupabase Auth + Postgres + StorageMulti-instance, several tenants
header-authTrusts reverse-proxy identity headersExisting SSO (Caddy, oauth2-proxy, Entra)

Pick one per slot with SELVA_AUTH_PROVIDER / SELVA_DATA_PROVIDER / SELVA_STORAGE_PROVIDER, then restart. Each defaults to local. Note header is auth-only; pair it with local or supabase for data and storage.

For a provider not shipped in the box, point SELVA_CONFIG_PATH at a .js file exporting a defineConfig() result. See Writing a provider.

The scoping rule

Every request carries a RequestContext: the caller’s identity and scope. One rule makes multi-tenancy safe: the query is the security boundary. Providers filter every read and write by that context, so an unauthorized caller gets empty results or an error, never someone else’s data.

Next