Open core
AgentArea is open core. The platform in this repository is Apache-2.0 licensed and runs standalone — you can self-host it, execute agents, govern tool calls, and never install anything commercial. Enterprise capability is added by installing an additional Python package alongside it. There is no second codebase, no fork, and noif enterprise: branch in the request path.
This page describes where the line sits, how the mechanism works, and which
parts of it are wired today versus merely declared.
The problem
Open-core products usually implement the split in one of two ways, and both age badly. Forking gives you two divergent codebases. Every bug fix lands twice or lands once and rots in the other. Behaviour drifts until nobody can reason about the open-source edition from the commercial one. Inline branching —if settings.ENTERPRISE: scattered through business
logic — keeps one codebase but makes every code path conditional. The
open-source reader cannot tell which branch is live, the test matrix doubles, and
the commercial logic sits in a public repository doing nothing.
Both make the boundary implicit. The boundary should be a named seam you can
enumerate.
How AgentArea approaches it
The mechanism: Python entry points
Core defines extension points by name. A separately installed package advertises factories for those names through the standard Python entry-point groupagentarea.extensions. At startup, core scans installed distributions and
registers whatever it finds:
discover_extensions() runs during startup in both apps/api and apps/worker.
Entry points map a name to a factory callable, not to an instance, so an
implementation can construct its own dependencies rather than accepting whatever
core would have injected.
The enterprise package lives in a sibling repository, agentarea-enterprise, and
declares its factories the ordinary way:
ExtensionRegistry.has(...) returns false and core
takes its own path.
The extension points core looks up
Two properties are worth noticing.
Core is complete without extensions. Each row has a working default. The
open-source edition is not a demo with holes in it.
An extension cannot silently override an explicit choice.
permissions is a
selector: if an operator sets ACCESS_CONTROL_BACKEND=openfga, core wires
OpenFGAPermissionService and logs a warning that the installed extension is
being ignored. This rule exists because the earlier order — extension first —
meant an installed Keto extension shadowed a configured OpenFGA backend, so
OpenFGA never enforced while the configuration claimed it did. An extension is a
fallback for an unmade decision, never a veto over a made one.
What DEPLOYMENT_MODE does and does not do
DEPLOYMENT_MODE selects DeploymentMode.OSS or DeploymentMode.ENTERPRISE
and configures a FeatureService. Its own docstring bounds its scope:
This controls UI/presentation concerns only. Implementation swapping (e.g., which PermissionService) is handled by the plugin extension registry, not feature flags.Its properties are
show_system_entity_badge, system_entities_read_only_in_ui,
enable_usage_metering, show_governance_overlay, and enable_network_rebac.
Setting DEPLOYMENT_MODE=enterprise without installing the enterprise package
changes what the interface displays. It does not add enforcement.
Where the line currently sits
Core includes more than the original open-core plan reserved for it. Relationship -based access control was once positioned as a commercial differentiator; it is now in core.OpenFGAPermissionService and KetoPermissionService both ship in
agentarea_common, and the enterprise package’s Keto permissions extension was
removed as redundant.
The line follows a rule rather than a price list: core is everything needed to
run governed agents on your own infrastructure. Commercial is what a
multi-customer or regulated operator needs on top — metering, entitlement, and
forwarding governance evidence into systems core has no business knowing about.
Why not put everything in the open-source edition?
Then there is no business, and an unfunded platform is a worse deal for a self-hosting team than a funded one with a boundary. The relevant question is whether the boundary is drawn somewhere that damages the open edition, and the test is: can you run this in production, governed, without paying? Workspace isolation, ReBAC authorization, sandbox isolation, budget enforcement, approvals, and audit persistence are all in core. What is held back is metering, plan entitlement, and forwarding evidence outward. The failure mode to watch for is a vendor moving a security control behind the paywall after adoption. AgentArea has moved in the other direction so far, with ReBAC arriving in core. That is one data point, not a guarantee.Why not feature flags instead of entry points?
A feature flag needs the code it gates to be present. Gating commercial logic with a flag means shipping that logic in a public repository and asking a boolean to protect it, which protects nothing and clutters the open codebase with paths its readers cannot use. Entry points make absence the default. The enterprise code is not in the image unless installed, so the open-source reader sees exactly the code that runs, and the commercial package is a normal Python distribution — versioned, pinned, and auditable by the customer who installs it. The costs are real. The seam has to be designed before it can be extended, so a capability with no extension point cannot be added without changing core. Entry points resolve at import time, which makes failures startup-time rather than request-time. And discovery is implicit: installing a package changes runtime behaviour with no configuration edit, which is why the startup logs name the implementation chosen for every selector point.Limits
- Not every declared extension is wired.
egress_enforceris advertised by the enterprise package and has a core-side port (agentarea_common/ports/egress_enforcer.py, with a no-op default), but no core code resolves it from the registry yet. Installing enterprise does not currently enforce container egress. - Audit forwarding is best-effort. The event is persisted first, then forwarded. If the sink raises, the failure is logged as a warning and the request proceeds. A reachable database with an unreachable SIEM loses forwarded events, not recorded ones — do not treat sink delivery as guaranteed.
DEPLOYMENT_MODE=enterprisealone enforces nothing. Without the package installed it changes presentation only.- A failed extension load does not stop startup.
discover_extensions()catches per-entry-point exceptions and continues. A broken enterprise package yields a running server with core defaults. Check startup logs forFailed to load extensionrather than assuming a clean boot means a loaded extension. - There is no license enforcement in core. Nothing checks entitlement to run the platform; the commercial boundary is what the package provides, not a runtime check that restricts core.
- The extension surface is small. Four names are resolved by core today. This is not a general plugin API for third-party functionality.
Related
- Workspaces, projects, and resources — the model both editions share
- Control plane and data plane — the boundary that makes customer-hosted execution possible
- Policy engine — the pipeline the entitlement gate joins
- Audit — what is recorded before anything is forwarded