Skip to main content

Features System

The features system controls tenant access to platform capabilities, supporting both boolean flags (on/off) and numeric limits (quotas). Features are granted at the plan level, may be overridden per-tenant, and a separate set of platform-level kill switches can disable them globally.

Data Model

Three tables define the features system:

TablePurpose
pla_featuresFeature definitions (slug, type, attributes)
pla_plan_featuresFeatures included in each plan
pla_tenant_featuresPer-tenant customizations (overrides)

The flow is: Feature Definition → Plan Assignment → Tenant Override

Feature Types

Boolean Features

Control access to capabilities. Examples: pwa, automations, storefront_search.

  • Stored as 'enabled' or 'disabled' in the database.
  • Runtime value: 'enabled' or 'disabled' (use the VALUE_ENABLED / VALUE_DISABLED constants on PlaFeature).
  • Identified by has_value = false in pla_features.

Limit Features

Define numeric quotas. Examples: admin_limit, issues_limit, maximum_discount_coupon_amount_limit.

  • Stored as a numeric string or 'unlimited' in the database.
  • Runtime value: integer or 'unlimited' (use the VALUE_UNLIMITED constant on PlaFeature).
  • Identified by has_value = true in pla_features.

Value Semantics

Runtime values use semantic strings from the feature model's value constants:

DB ValueFeature TypeRuntime ValueMeaning
'enabled'booleanVALUE_ENABLEDFeature is on
'disabled'booleanVALUE_DISABLEDFeature is off
'unlimited'limitVALUE_UNLIMITEDNo cap
'500'limit500 (int)Capped at 500

Resolution Flow

Feature access is not a single linear waterfall. The check first decides whether the feature is platform-controlled, and the two branches share only the early-exit rules.

Shared early exits

  1. Deprecating features are always enabled. The feature flag stays in code while the functionality is being absorbed into mandatory core behavior; the check returns true regardless of plan or tenant.
  2. Forcefully disabled custom feature returns false. A pla_tenant_features row with value 'disabled' overrides every later step, including plan inclusion.

Platform-feature branch (short-circuit)

If the feature appears in platform.feature_control, the check returns the platform-level result directly: the global config flag for that slug, plus the crawler-blocking rule for search and recommendation features. Plan, trial, and custom-enable rules do not run for platform features. This is what makes platform features true kill switches; a tenant cannot recover access via a custom override. See Platform Features for the catalog and Vapor-side workflow.

Standard branch (non-platform features)

If the feature is not platform-controlled, the check evaluates in order:

  1. Trial enables the feature if accessible_during_trial = true and the tenant has no plan yet.
  2. Custom tenant feature (pla_tenant_features) enables the feature when present and not 'disabled'.
  3. Plan feature (pla_plan_features) enables the feature when present and not 'disabled'.
  4. Default: denied.

Custom tenant features can add features not in the tenant's plan and can disable features that the plan otherwise includes.

Feature Attributes

Each feature definition has attributes controlling behavior:

AttributePurpose
accessible_during_trialAllow access during trial period
displayable_publiclyShow on billing/features page
value_can_be_unlimitedAllow 'unlimited' for limit features
default_valueFallback when not explicitly set

Caching

Feature caching is part of the broader Tenant Resolver system.

Quick overview

  • Features are computed by the tenant resolver as part of the unified payload.
  • Results are cached in Valkey (700-1000 second TTL) inside the tenant payload.
  • $tenant->available_features returns the cached payload when the tenant was loaded via the resolver (the normal request path).
  • $tenant->resolved_features provides in-memory caching scoped to the current request, useful for tenants loaded outside the resolver (jobs, console).

Invalidation triggers

  • Plan features change: the plan-feature observer invalidates affected tenants.
  • Tenant features change: the tenant-feature observer invalidates that tenant.
  • Manual flush: the unified query service exposes a per-ID flush method for emergency invalidation.

Constraints

The feature constraints registry applies validation rules to specific features:

// Example: coupon discount capped at 100
'maximum_discount_coupon_amount_limit' => ['max' => 100]

Constraints are applied automatically when saving a plan-feature or tenant-feature row.

X

Graph View