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:
| Table | Purpose |
|---|---|
pla_features | Feature definitions (slug, type, attributes) |
pla_plan_features | Features included in each plan |
pla_tenant_features | Per-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 theVALUE_ENABLED/VALUE_DISABLEDconstants onPlaFeature). - Identified by
has_value = falseinpla_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 theVALUE_UNLIMITEDconstant onPlaFeature). - Identified by
has_value = trueinpla_features.
Value Semantics
Runtime values use semantic strings from the feature model's value constants:
| DB Value | Feature Type | Runtime Value | Meaning |
|---|---|---|---|
'enabled' | boolean | VALUE_ENABLED | Feature is on |
'disabled' | boolean | VALUE_DISABLED | Feature is off |
'unlimited' | limit | VALUE_UNLIMITED | No cap |
'500' | limit | 500 (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
- Deprecating features are always enabled. The feature flag stays in code while the functionality is being absorbed into mandatory core behavior; the check returns
trueregardless of plan or tenant. - Forcefully disabled custom feature returns
false. Apla_tenant_featuresrow 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:
- Trial enables the feature if
accessible_during_trial = trueand the tenant has no plan yet. - Custom tenant feature (
pla_tenant_features) enables the feature when present and not'disabled'. - Plan feature (
pla_plan_features) enables the feature when present and not'disabled'. - 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:
| Attribute | Purpose |
|---|---|
accessible_during_trial | Allow access during trial period |
displayable_publicly | Show on billing/features page |
value_can_be_unlimited | Allow 'unlimited' for limit features |
default_value | Fallback 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_featuresreturns the cached payload when the tenant was loaded via the resolver (the normal request path).$tenant->resolved_featuresprovides 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.
Related Documentation
- Platform Feature Flags (Product) - Business-level feature descriptions, categories, and availability.
- Platform Features - The emergency-control catalog and Vapor workflow for platform-level kill switches.