Skip to content
Concept

Extension Model

Choose an App contribution, publish it through the manifest, and understand how Core makes it available.

Extension Model

Choose the capability

To add a platform feature, implement its SDK contribution interface in your App's declared contribution directory. Core discovers it during manifest building and passes its declarations to the owning runtime.

Your taskImplementation guide
Publish a Resource and its permissionsCreate a Resource
Add an App menu destinationAdd a menu destination
Add settingsAdd a settings page
Add a dashboard or slot widgetAdd a Dashboard Widget, Add a Slot Widget
Let another App reference recordsResource References
Connect a process, approval, or signatureBusiness Process, Electronic Approval, Electronic Signature

For exact interfaces, return types, and descriptor parameters, use Contribution contracts. You do not need to learn every contribution family before implementing one.

Follow the declaration

Manifest locations → classes implementing SDK interfaces → generated contribution index → runtime catalog → tenant and actor checks → App behavior.

The generated src/WorkshopAppManifest.php supplies namespace/directory pairs. In this method excerpt, paths are relative to that class, so the package does not depend on a Core checkout layout.

Code example
PHP
public function contributionLocations(): array
{
    return [
        ['namespace' => 'Amuzcorp\Nexia\Workshop\Contribution', 'directory' => __DIR__.'/Contribution'],
        ['namespace' => 'Amuzcorp\Nexia\Workshop\Descriptors', 'directory' => __DIR__.'/Descriptors'],
    ];
}

A class must explicitly implement the interface. A trait that supplies its methods does not make the class discoverable. Subdirectories are included. Declaration methods return static metadata; they must not query tenant rows, read the current actor, or perform writes. Runtime providers receive the context their contract declares when Core invokes them.

After changing contribution classes, rebuild the runtime manifests:

Code example
Shell
task artisan -- nexia-runtime:refresh-runtime-caches
task artisan -- nexia-apps:doctor-package-app workshop

The doctor should discover the expected interfaces without failures. These checks establish wiring, not user access. A long-running process may also need a restart; see Fix contribution discovery.

A generated Resource Module holds the facets for one Resource: stable key, model binding, authorization, permissions, navigation, and public descriptor. It does not become a package-wide registry. Non-Resource capabilities can live in dedicated classes, grouped by the runtime they serve.

A contributor is the discoverable class. A descriptor is a typed value it publishes. AppDescriptorContribution::appDescriptors() returns an AppDescriptorSet; host consumers select the descriptor families they support. Some provider-bearing interfaces also supply App behavior for runtime calls.

The Resource's catalog entry describes the Resource type, not its tenant rows. An App's editable master data belongs in its model and API. Catalog keys appear in permissions, translations, and configuration; choose them before publishing and do not rename them as a cosmetic change.

Choose who owns the result

KindResultWhat a later release changes
CatalogCode-owned capability definitionsDefinitions after synchronization/cache refresh
DescriptorTyped runtime metadata with stable identitySupported future resolution; existing references need compatibility
Role presetAn administrator's starting permission selectionFuture selections, not existing grants
Copyable templateTenant-owned content created on explicit applicationFuture copies, not existing tenant edits
Required installation dataMinimum tenant rows needed to operateOnly through an explicit safe migration or supported repair path

A Process Template descriptor supplies an editor starting point; saving and publishing produces the tenant's definition. It is distinct from applying a copyable template that writes rows and records the application.

Use Role Presets, Copyable templates, and Required installation content for their separate procedures. None of these mechanisms grants authority by itself.

Descriptor lifecycle and versions

A descriptor's version is separate from the App package release. The owning runtime decides which identity and version it persists. Do not assume every family pins versions in the same way.

StatusNew selectionExisting work
ActiveEligible, subject to runtime gatesMay resolve
DeprecatedExcluded from new authoringMay still resolve where the runtime supports existing references
RemovedUnavailableResolution is refused

Retire a capability by deprecating it, draining or explicitly migrating its references, then removing it. Deleting its source early can break running work. Process external tasks, published definitions, approval bindings, and template copies have different persistence rules; follow the specific Business Process contracts or Electronic Approval contracts.

A Resource lifecycle event is a facet of its parent Resource descriptor and has an integer payload schemaVersion. A standalone public integration event has its own event declaration. Neither version substitutes for the Composer package tag. See Release an App.

Discovery and access are separate

CheckQuestion
DiscoveryIs the class indexed under the required interface?
App availabilityIs its owner operational for this tenant?
PermissionDoes this capability require a permission, and does the actor hold it?
Lifecycle and compatibilityCan this runtime use this descriptor for this operation?

Permission and lifecycle behavior varies by surface; a nullable navigation permission and an existing process task are different cases. The final App handler must still validate inputs, authorize the record, and enforce business state. If the feature is missing, diagnose these layers in Fix contribution discovery.

Source of truth: docs/developers/content/en/architecture/extension-model.md