Back to Bytebase

SaaS vs Self-Hosted Behavior Differences

docs/plans/saas/06.saas-vs-selfhosted.md

3.17.17.5 KB
Original Source

SaaS vs Self-Hosted Behavior Differences

This document catalogs every code path where SaaS and self-hosted modes diverge, based on profile.SaaS checks.


1. User Management

CreateUser — backend/api/v1/user_service.go:192

  • SaaS: Blocked (CodeUnimplemented). Users are added via workspace IAM policy.
  • Self-hosted: Allowed with bb.users.create permission.

UpdateUser (other user) — backend/api/v1/user_service.go:338

  • SaaS: Only self-updates allowed. Updating another user's profile returns CodePermissionDenied because principals are global (shared across workspaces).
  • Self-hosted: Admins with bb.users.update permission can update other users.

DeleteUser — backend/api/v1/user_service.go:490

  • SaaS: Blocked (CodeUnimplemented).
  • Self-hosted: Allowed with permission. Checks that at least one workspace admin remains.

UndeleteUser — backend/api/v1/user_service.go:577

  • SaaS: Blocked (CodeUnimplemented).
  • Self-hosted: Allowed with permission and license checks.

UpdateEmail — backend/api/v1/user_service.go:630

  • SaaS: Blocked (CodeUnimplemented).
  • Self-hosted: Allowed with bb.users.updateEmail permission.

hasExtraWorkspaceAdmin — backend/api/v1/user_service.go:555

  • SaaS: allUsers binding is never counted as admin coverage.
  • Self-hosted: allUsers binding means every principal is an admin; counts all active principals via CountAllActivePrincipals.

2. Authentication & Signup

Signup — Workspace Resolution — backend/api/v1/auth_service.go:199-230

Finding workspaces (line 199):

  • SaaS: FindWorkspacesByMemberEmail(ctx, email, false) — only explicit membership.
  • Self-hosted: FindWorkspacesByMemberEmail(ctx, email, true) — includes allUsers workspaces.

Fallback when not a member (line 208-217):

  • SaaS: No fallback. If not explicitly a member, workspaceID stays empty → new workspace created.
  • Self-hosted: Falls back to store.GetWorkspaceID() to auto-join the single existing workspace.

DisallowSignup check (line 220-229):

  • SaaS: Skipped entirely.
  • Self-hosted: Checks workspace setting disallow_signup (gated by FEATURE_DISALLOW_SELF_SERVICE_SIGNUP license).

Login — Workspace Resolution — backend/api/v1/auth_service.go:947

  • SaaS: FindWorkspaceIDByMemberEmail(ctx, email, false) — explicit membership only.
  • Self-hosted: FindWorkspaceIDByMemberEmail(ctx, email, true) — includes allUsers.

Auth Interceptor — backend/api/auth/auth.go:251

  • SaaS: allUsers is never a valid principal for workspace membership.
  • Self-hosted: allUsers is valid — any principal is considered a member.

3. Workspace IAM

SetWorkspaceIamPolicy — backend/api/v1/workspace_service.go:70

  • SaaS: Rejects any binding containing allUsers with error: "allUsers is not allowed in workspace IAM policy in SaaS mode, add members explicitly".
  • Self-hosted: allUsers allowed in bindings.

IAM Permission Check — backend/component/iam/manager.go:51

  • SaaS: At workspace level, allUsers is skipped (skipAllUsers=true). At project level, allUsers means "all workspace members" (still allowed).
  • Self-hosted: allUsers grants permission at both workspace and project levels.

4. Settings

disallow_signup — backend/api/v1/setting_service.go:210

  • SaaS: Cannot be modified. Returns "feature unavailable in current mode".
  • Self-hosted: Modifiable (gated by license feature FEATURE_DISALLOW_SELF_SERVICE_SIGNUP).

external_url — backend/api/v1/setting_service.go:220

  • SaaS: Cannot be modified. Managed by SaaS operator.
  • Self-hosted: Modifiable via UI (unless already set by CLI flag --external-url).

IsFeatureUnavailable — backend/component/config/profile.go:63

  • Uses saasFeatureControlMap (currently empty). Reserved for future SaaS-specific feature gating.

5. Subscription & Licensing

UpdateSubscription — backend/api/v1/subscription_service.go:42

  • SaaS: Blocked. License managed by operator.
  • Self-hosted: Allowed via StoreLicense().

CheckReplicaLimit — backend/enterprise/license.go:414

  • SaaS: Skipped entirely (return nil). Replica scaling managed by operator.
  • Self-hosted: Enforces replica limit based on HA license. Without HA, max 1 replica.

6. Instance Management

IAM Credential Validation — backend/api/v1/instance_service.go:281

  • SaaS: Default IAM credentials (Google Cloud SQL, AWS RDS, Azure) are prohibited. Requires explicit credentials with iam_extension set.
  • Self-hosted: All IAM credential types allowed including default credentials.

7. Sample Instances

Manager Initialization — backend/server/server.go:157

  • SaaS: sampleInstanceManager is not initialized.
  • Self-hosted: Initialized and loads workspace-dependent settings.

SetupSample RPC — backend/api/v1/actuator_service.go:135

  • SaaS: Returns empty response immediately.
  • Self-hosted: Executes sample data generation.

8. OAuth2

Legacy Routes — backend/api/oauth2/oauth2.go:67

  • SaaS: Legacy routes (without workspace ID in URL) are not registered.
  • Self-hosted: Legacy routes (/api/oauth2/register, /api/oauth2/authorize, etc.) are registered for backwards compatibility.

Workspace Resolution — backend/api/oauth2/oauth2.go:82

  • SaaS: Requires explicit workspace ID in URL parameter. Returns error if missing.
  • Self-hosted: Falls back to store.GetWorkspaceID() if workspace ID not in URL.

9. Actuator / System Info

GetActuatorInfo — backend/api/v1/actuator_service.go:60

  • SaaS: Uses workspace from auth context (URL). No store fallback.
  • Self-hosted: Falls back to store.GetWorkspaceID() to get the single workspace.

ActuatorInfo Response — backend/api/v1/actuator_service.go:160

  • Returns Saas: true/false in the response so the frontend can adjust its behavior.

10. Identity Providers

ListIdentityProviders — backend/api/v1/idp_service.go:59

  • SaaS: Requires workspace ID from auth context. No fallback.
  • Self-hosted: Falls back to store.GetWorkspaceID() for unauthenticated login page (the endpoint is allow_without_credential).

Summary Table

AreaSelf-HostedSaaS
CreateUserAllowed (with permission)Blocked — use workspace IAM
UpdateUser (other)Allowed (with permission)Self-updates only
DeleteUser / UndeleteUser / UpdateEmailAllowed (with permission)Blocked
Signup: workspace resolutionIncludes allUsers, auto-joins existingExplicit membership only, creates new workspace
Signup: disallow_signupEnforced (with license)Skipped
Workspace IAM: allUsersAllowedBlocked
IAM permission: allUsersGrants permission at workspace + project levelSkipped at workspace level, allowed at project level
disallow_signup settingConfigurableCannot modify
external_url settingConfigurableCannot modify
License updatesAllowedBlocked
Replica limitEnforced by licenseSkipped (operator-managed)
IAM credentialsDefault credentials allowedExplicit credentials required
Sample instancesAvailableDisabled
OAuth2 legacy routesRegisteredNot registered
Workspace ID resolutionFalls back to storeRequired in URL/auth context