Edition-Based Feature Gating#
Dify restricts or enables features based on which of three deployment editions is active: COMMUNITY (default self-hosted), ENTERPRISE (self-hosted with enterprise license), and CLOUD (hosted SaaS). The edition is a single StrEnum defined in api/enums/deployment_edition.py and is the single source of truth for all gating decisions across the backend and frontend. It supersedes the now-removed ENTERPRISE_ENABLED and BILLING_ENABLED flags.
Edition Detection#
Backend#
DEPLOYMENT_EDITION is an environment variable read into DeploymentConfig in api/configs/deploy/__init__.py, with a default of DeploymentEdition.COMMUNITY. All backend code imports dify_config.DEPLOYMENT_EDITION and compares it directly against DeploymentEdition enum members.
Frontend#
The backend exposes deployment_edition in the unauthenticated /system-features endpoint via FeatureService.get_system_features(). The frontend fetches this at app startup via systemFeaturesQueryOptions() (a TanStack Query wrapper with staleTime: Infinity), caching the value for the full session. PR #39454 centralized this: the Root Layout now prefetches and hydrates system features server-side, eliminating ambiguous client-side environment checks.
There is also an EDITION β NEXT_PUBLIC_EDITION mapping applied by web/docker/entrypoint.sh, but consuming components use the system-features query path as the authoritative source.
Similarly, NEXT_PUBLIC_ENABLE_AGENT_V2 controls Agent V2 route availability entirely β feature-guard.ts calls notFound() if this env var is falsy , while web/features/agent-v2/feature-flag.ts exports isAgentV2Enabled() that reads it directly. This is a deploy-time build flag, separate from the runtime edition check.
Backend API Guards#
Edition guards in api/controllers/console/wraps.py are applied as route decorators:
| Decorator | HTTP response when condition fails | Condition |
|---|---|---|
only_edition_cloud | 404 | edition β CLOUD |
only_edition_enterprise | 404 | edition β ENTERPRISE |
only_edition_self_hosted | 404 | edition == CLOUD (blocks cloud; allows COMMUNITY + ENTERPRISE) |
These are pure edition guards β distinct from the cloud_edition_billing_* family, which enforce subscription quota limits within CLOUD edition.
Additional inline checks against dify_config.DEPLOYMENT_EDITION appear outside decorators. For example, in tool_providers.py, the _resolve_identity_mode() helper coerces any non-OFF MCP identity-forwarding mode back to OFF unless DEPLOYMENT_EDITION == DeploymentEdition.ENTERPRISE β an enterprise-only capability enforced at the API layer.
Frontend UI Gating#
React components read deployment_edition from the cached system-features query and branch accordingly. The canonical pattern is:
const { data: deploymentEdition } = useSuspenseQuery({
...systemFeaturesQueryOptions(),
select: (systemFeatures) => systemFeatures.deployment_edition,
})
const previewEnabled = deploymentEdition !== 'COMMUNITY'
Agent V2 Preview mode β gated in web/features/agent-v2/agent-detail/configure/page.tsx:
previewEnabledistruefor CLOUD and ENTERPRISE;falsefor COMMUNITY.?mode=previewURL params are silently overridden tobuildon COMMUNITY.- The Preview tab renders as a tooltip-wrapped disabled button when
previewEnabledis false. - ENTERPRISE always has preview enabled regardless of license validity status.
PR #39399 introduced the pattern of blocking preview mode on COMMUNITY edition.
Key Source Files#
| File | Purpose |
|---|---|
api/enums/deployment_edition.py | DeploymentEdition StrEnum (COMMUNITY, ENTERPRISE, CLOUD) |
api/configs/deploy/__init__.py | DEPLOYMENT_EDITION env var config (default: COMMUNITY) |
api/services/feature_service.py | get_system_features() β surfaces deployment_edition to clients |
api/controllers/console/wraps.py | only_edition_cloud/enterprise/self_hosted decorators |
web/features/agent-v2/agent-detail/configure/page.tsx | Preview mode edition gate (canonical frontend pattern) |
web/app/(commonLayout)/agents/feature-guard.ts | Agent V2 route guard (NEXT_PUBLIC_ENABLE_AGENT_V2) |
web/features/agent-v2/feature-flag.ts | isAgentV2Enabled() helper |
Relationship to Other Gating Systems#
- Billing/quota checks (
cloud_edition_billing_*decorators): enforce subscription plan limits within CLOUD edition only. Not edition gates by themselves. - Enterprise branding (
SystemFeatureModel.branding): gated separately onDEPLOYMENT_EDITION == ENTERPRISEinsideget_system_features(). See the Branding Customization article. - RBAC: controlled by its own permission decorators; RBAC is disabled by default in COMMUNITY edition and falls back to Owner/Admin-only checks.