1. Scoped Action Enablement
Scoped action enablement is the framework-level answer to the question:
"Why is this action unavailable right now?"
Permission rules already answer:
"May this identity perform area / functionalDomain / action?"
Action enablement adds the missing operational layer so applications, shells, and setup dashboards can also answer:
-
is the action allowed by policy?
-
is the action enabled for this rollout or cohort?
-
is the action operationally ready, or is some setup still missing?
This guide documents the runtime API, manifest model, SPI contracts, and the framework’s built-in dependency types.
1.1. Core principle
The atomic unit is still the same scoped action tuple already used by Quantum security:
area / functionalDomain / action
Examples:
-
integration / exchange / create -
integration / guided_integration / create -
integration / workflow / create -
system / action-enablement / check
Quantum intentionally does not introduce a second authorization abstraction. The same tuple defined by @FunctionalMapping and @FunctionalAction remains canonical for:
-
permission checks
-
UI action derivation
-
action enablement manifests
-
operational blocker reporting
Capabilities are still useful, but they are an application-level grouping over one or more scoped actions, not a replacement for the scoped action tuple itself.
1.2. What the framework returns
Each evaluated action produces four booleans:
| Field | Meaning |
|---|---|
|
The permission rule stack allows the action for the evaluated identity and data domain |
|
Rollout and administrative toggles, especially feature flags, say the action should be exposed |
|
Operational prerequisites such as settings, referenced entities, or external runtime dependencies are satisfied |
|
Convenience result equal to |
This separation is important because it points the user to the right remediation path:
-
allowed=falseusually means a policy or role issue -
enabled=falseusually means a feature flag or rollout issue -
ready=falseusually means configuration, provisioning, or runtime setup is incomplete
1.3. Runtime models
The runtime contracts live in quantum-action-enablement-models.
public class ScopedActionRef {
String area;
String functionalDomain;
String action;
}
public class DependencyCheckRef {
String type;
String refName;
Map<String, Object> config;
}
public class ScopedActionRequirement {
ScopedActionRef scopedAction;
String displayName;
String description;
List<DependencyCheckRef> dependencies;
}
public class ScopedActionEnablementStatus {
ScopedActionRef scopedAction;
boolean allowed;
boolean enabled;
boolean ready;
boolean usable;
List<EnablementBlocker> blockers;
}
Each blocker is machine-readable and user-displayable:
public class EnablementBlocker {
EnablementImpact impact; // ALLOWED, ENABLED, READY
String type; // permission, feature-flag, setting-present, ...
String code; // permission-denied, setting-missing, ...
String message; // human-readable explanation
String severity; // warn or error
Map<String, Object> metadata;
}
1.4. REST API
The runtime API lives in quantum-action-enablement-quarkus and is exposed by ActionEnablementResource.
Endpoints:
-
POST /system/actions/enablement/check -
GET /system/actions/enablement/manifest
The resource itself is mapped as:
-
area=system -
functionalDomain=action-enablement -
action=check|view
1.4.1. Check request
{
"identity": "tenant-admin",
"realm": "acme",
"roles": ["admin"],
"orgRefName": "acme-org",
"accountNumber": "1000000001",
"tenantId": "acme-tenant",
"dataSegment": 0,
"ownerId": "tenant-admin",
"scope": "api",
"actions": [
{
"area": "integration",
"functionalDomain": "guided_integration",
"action": "create"
},
{
"area": "integration",
"functionalDomain": "workflow",
"action": "create"
}
]
}
1.4.2. Check response
{
"results": [
{
"scopedAction": {
"area": "integration",
"functionalDomain": "guided_integration",
"action": "create"
},
"allowed": true,
"enabled": false,
"ready": false,
"usable": false,
"blockers": [
{
"impact": "ENABLED",
"type": "feature-flag",
"code": "feature-flag-disabled",
"message": "Feature flag 'guided-integrations' is disabled.",
"severity": "warn"
},
{
"impact": "READY",
"type": "setting-present",
"code": "setting-missing",
"message": "Configuration setting 'quantum.integration.shared-storage.root' is not set.",
"severity": "error"
}
]
}
]
}
1.4.3. Manifest response
GET /system/actions/enablement/manifest returns the registered ScopedActionRequirement entries after all CDI contributors have been aggregated.
That endpoint is useful for:
-
setup dashboards
-
shell tools
-
test fixtures
-
verifying what requirements are active for a given application build
1.5. Manifest model and contributor SPI
The dependency relationships between settings, feature flags, and scoped actions are expressed as ScopedActionRequirement entries.
Applications contribute requirements through CDI:
public interface ScopedActionRequirementContributor {
default int priority() {
return 100;
}
Collection<ScopedActionRequirement> requirements();
}
The framework aggregates contributors through ScopedActionRequirementRegistry:
public interface ScopedActionRequirementRegistry {
Optional<ScopedActionRequirement> find(ScopedActionRef ref);
List<ScopedActionRequirement> list();
}
This is where you encode relationships such as:
-
integration / guided_integration / createdepends on feature flagguided-integrations -
integration / exchange / createdepends on tenant filesystem provisioning -
system / export / viewdepends on a specific config property being present
1.5.1. Contributor precedence
Contributors are sorted by ascending priority(). Requirements are then indexed by scoped action key.
Practical implication:
-
later contributors for the same scoped action replace earlier entries
-
the framework’s own contributor can provide a default manifest entry
-
applications can override or specialize that entry with their own contributor
1.6. Resolver SPI
Each dependency type is resolved by an ActionDependencyResolver:
public interface ActionDependencyResolver {
String supportsType();
DependencyResolutionResult evaluate(
DependencyCheckRef dependency,
EnablementEvaluationContext context
);
}
EnablementEvaluationContext carries the runtime inputs:
-
identity -
realm -
roles -
scope -
DataDomain -
target
ScopedActionRef
This gives each resolver enough context to evaluate readiness in the correct tenant, realm, and user scope.
1.7. Built-in OSS dependency types
The open-source framework currently ships with these resolver types:
| Dependency type | Meaning | Impact |
|---|---|---|
|
Run the existing permission engine for the scoped action |
|
|
Require that a |
|
|
Require that a named configuration property exists and is non-blank |
|
|
Require that a referenced entity exists in the current realm |
|
Representative blocker codes include:
-
permission-denied -
feature-flag-ref-missing -
feature-flag-missing -
feature-flag-disabled -
setting-name-missing -
setting-missing -
entity-dependency-invalid -
entity-model-unresolved -
entity-missing
1.8. Evaluation behavior
ScopedActionEnablementService evaluates dependencies for each requested action and aggregates blockers by impact.
At a high level:
-
Resolve the manifest entry for the scoped action.
-
Build an
EnablementEvaluationContextfrom the request plus current security context defaults. -
Resolve each dependency by type.
-
Collect blockers from all failing dependencies.
-
Derive
allowed,enabled,ready, andusable.
Important framework behavior:
-
If no manifest entry exists, the service creates a synthetic fallback requirement with only a
permissiondependency. -
The response also includes a
manifest-missingblocker withimpact=READY. -
If a dependency type has no registered resolver, the response includes
unsupported-dependency-type.
This means action enablement is designed to fail visibly, not silently.
1.9. Example contributor
The framework itself contributes manifest entries for the action-enablement API. Applications then contribute their own operational requirements.
@ApplicationScoped
public class MyActionEnablementManifestContributor
implements ScopedActionRequirementContributor {
@Override
public Collection<ScopedActionRequirement> requirements() {
return List.of(
ScopedActionRequirement.builder()
.scopedAction(ScopedActionRef.builder()
.area("integration")
.functionalDomain("guided_integration")
.action("create")
.build())
.displayName("Generate Guided Integration")
.description("Requires policy, rollout enablement, and baseline setup.")
.dependencies(List.of(
DependencyCheckRef.builder().type("permission").build(),
DependencyCheckRef.builder()
.type("feature-flag")
.refName("guided-integrations")
.build(),
DependencyCheckRef.builder()
.type("setting-present")
.refName("quantum.integration.shared-storage.root")
.build()
))
.build()
);
}
}
This is the practical link between settings and capabilities:
-
the capability is what the product or UI wants to describe
-
the scoped action is the framework key
-
the manifest records which settings, feature flags, and runtime dependencies attach to that scoped action
1.10. Relationship to permissions and UI actions
Action enablement complements existing security behavior rather than replacing it.
-
@FunctionalMappingand@FunctionalActiondefine the scoped action tuple. -
Permission rules decide whether the action is authorized.
-
fillUIActions()still reports which actions are allowed from the permission perspective. -
Action enablement explains why the action should still be disabled or annotated because rollout or readiness conditions are not met.
This makes it especially useful for:
-
onboarding flows
-
setup dashboards
-
admin consoles
-
explainable disabled buttons
-
CLI and MCP tooling
1.11. Enterprise extensions
The framework module is intentionally generic. Enterprise modules can contribute deeper operational resolvers through the same ActionDependencyResolver SPI.
Current enterprise resolvers include checks such as:
-
managed-secret-configured -
tenant-filesystem-provisioned -
standard-mounts-present -
workflow-runtime-reachable
Those enterprise resolvers answer questions like:
-
has the tenant filesystem been provisioned?
-
do required standard mounts exist?
-
is the managed secret configured?
-
can the workflow runtime be reached?
The framework documentation stops at the extension point and shared response model. Enterprise-specific operational semantics should be documented in the enterprise module.
1.12. Guidance
-
Keep authorization canonical in permission rules. Do not re-implement policy logic inside custom readiness resolvers.
-
Use
feature-flagfor rollout state, not for durable authorization. -
Use
setting-presentandentity-existsfor generic readiness checks. -
Put domain-heavy infrastructure checks in application or enterprise resolvers.
-
Keep blocker codes stable because UIs and automation may key off them.
-
Treat capabilities as UX groupings over scoped actions, not as a replacement for
area / functionalDomain / action.