Skip to main content

Licensing & Feature Entitlements

Feature row 8 — Sprint LIC Phase 1 / Sprint COMP-1

Every tenant has a plan tier. The entitlement system is always on.

Plans at a glance

Behavry ships with three plan tiers:

PlanForFeatures
TrialProspects running a POCCore enforcement + 3 features (Community Library, Custom DLP, Behavioral Baselines)
ProfessionalSecurity / platform teams running Behavry in productionTrial + 7 more (Inbound Rules, Context Gate, Blast Radius, Cost Attribution, SIEM & Webhooks, Discovery Connectors, Human AI Governance)
EnterpriseLarge orgs with multi-framework compliance needsProfessional + 7 more (Delegation Chains, Workflow Governance, Data Protection, Policy Writer AI, Compliance Modules, SAML SSO, Team Management)

Every agent identity, proxy decision, and audit event is available on every plan. The entitlement system only gates additional capabilities beyond the core.

The 17 features

The canonical list lives in backend/behavry/admin/entitlements.py under FEATURE_CATALOG:

KeyNamePlan
community_libraryCommunity Policy LibraryTrial+
custom_dlp_patternsCustom DLP PatternsTrial+
behavioral_baselinesBehavioral BaselinesTrial+
inbound_rulesInbound Rules EnginePro+
context_gateContext GatePro+
blast_radiusBlast Radius LimitsPro+
cost_attributionCost AttributionPro+
siem_webhooksSIEM & WebhooksPro+
discovery_connectorsDiscovery ConnectorsPro+
human_governanceHuman AI GovernancePro+
delegation_chainsDelegation ChainsEnterprise
workflow_governanceWorkflow GovernanceEnterprise
data_protectionData Protection PipelineEnterprise
policy_writer_aiPolicy Writer AI AssistEnterprise
compliance_modulesCompliance Modules (vertical frameworks)Enterprise
saml_ssoSAML 2.0 SSOEnterprise
team_managementTeam Management (roles + invites)Enterprise

How gating works at runtime

Every backend route that exposes a gated feature decorates itself with a feature check:

from behavry.admin.entitlements import require_feature

@router.get("/context-gate/summary")
async def summary(
_: None = Depends(require_feature("context_gate")),
):
...

require_feature() reads the authenticated tenant's plan tier, intersects with any tenant-level override stored in TenantConfig, and returns 403 feature_not_included if the feature isn't entitled. The check is cached per-request.

On the dashboard, the usePlanFeatures() React hook mirrors the same logic and hides UI that the tenant isn't entitled to see. Navigating directly to a gated route shows an "Upgrade to {plan}" drawer.

Super-admin bypass

Users with is_super_admin (fleet-level operators) bypass entitlement checks entirely. This is intentional: support engineers need to be able to inspect any tenant's data regardless of plan. Bypass events are still audited.

Overrides

Enterprise customers can get specific features early (e.g. policy_writer_ai on Pro). Super-admins set these via:

POST /api/v1/tenants/{id}/features
{ "enable": ["policy_writer_ai"] }

Overrides are stored in TenantConfig.feature_overrides (JSONB) and layer over the plan-tier defaults.

API

Routes: backend/behavry/admin/license_routes.py.

MethodPathPurpose
GET/api/v1/licenseCurrent tenant's plan tier, features, expiry
POST/api/v1/license/activateActivate a license key
GET/api/v1/plan-featuresAll features grouped by plan (used by the dashboard)
GET/api/v1/tenants/{id}/featuresSuper-admin: inspect a tenant's effective features
POST/api/v1/tenants/{id}/featuresSuper-admin: add/remove feature overrides