SDK contracts
Find public PHP capabilities by namespace and distinguish host calls, App contributions, and direct-use values.
SDK contracts
Choose the namespace for your task below. Consume means inject a host capability; implement means publish an App contribution; construct means use a value, helper, or adapter directly. A namespace can contain more than one kind.
Use a host capability
use Nexia\Tenancy\Contracts\TenantSettings;
final class ReportClock
{
public function __construct(private TenantSettings $settings) {}
public function timezone(): string
{
return $this->settings->businessTimezone();
}
}Resolve ReportClock through Laravel in a restored tenant context. It returns that
tenant's business timezone through the host binding. For Resource authorization,
start with Resource contracts; for frontend imports, use
React components and hooks.
PHP namespace map
| Namespace | How the App uses it | Contents |
|---|---|---|
Nexia\AppRuntime | construct / implement | AbstractPackageAppManifest, AppDefinition, AppPackageMetadataReader, manifest contracts |
Nexia\Contribution | implement / construct | ResourceCatalogContribution, InheritedSearchVisibilityContribution, ResourceAuthorizationContract, ResourceRecordOwner |
Nexia\AppDescriptors | implement / construct / consume | Descriptor contributions, public lifecycle-event schemas, and strict contribution validation |
Nexia\Agent | construct / implement | App-owned route-backed Agent tool declarations and app-neutral routing, effect, surface, lane, executor, and tier values |
Nexia\Permission | construct / implement | AssignmentScope, PermissionContribution, PermissionDefinition, RolePresetContribution, LegalEntityMemberSelfAccessContribution, PresetType, GrantControl, SubjectPopulation |
Nexia\Access | construct | App-neutral AuthorizationContext and RoleCatalog values |
Nexia\Laravel\Access | consume / reuse | Laravel host contracts ResourceAuthorization, PermissionAuthorizer, SubjectPermissionAuthorizer, TenantPermissionSnapshotAuthorizer, and the EvaluatesPermissionDecision concern |
Nexia\Laravel\Identity | consume | Host Party lookup and query composition without exposing the Core Party model |
Nexia\Laravel\Models | extend / reuse | Laravel adapter bases and model concerns, plus the host-configured Scout identity and engine resolver registry; the app-neutral kernel has no model base namespace |
Nexia\Laravel\Filters | construct / implement | Laravel adapter filters and sorts: Exact, Callback, HasRelation, Column, RelationSort, and the Filter / Sort contracts |
Nexia\Laravel\Resources | construct | ResourceListFields, the unified search/filter/sort catalog consumed by Filterable |
Nexia\Laravel\ResourceReference | consume / construct | Host availability contract and paginator-to-ResourceReferencePage adapter |
Nexia\Navigation | implement / construct | NavigationContribution, NavigationItem, ShellNavigationLocateAction, destination concerns |
Nexia\Palette | implement / construct | PaletteCommandContribution, PaletteCommand, and executable-command helpers |
Nexia\Dashboard | implement / construct | DashboardWidget, DashboardWidgetKind, DashboardWidgetParameter, RendererCapability, query sources |
Nexia\Approval | consume / construct | Approval read models, frozen binding submissions, and host contracts |
Nexia\Process | consume / implement / construct | BPMN types, status enums, ProcessRuntime, instance snapshots, namespaced durable message delivery and receive-task correlation |
Nexia\Identity | consume / construct | Actor, Party directories, person-Party provisioning, actor-filtered Party access and relationship projections, and optional batch profile and invitation lookup capabilities |
Nexia\Security | consume | Host-owned recent-authentication proof for protected App actions |
Nexia\Documents | consume | Host-bundled local PDF font assets and their provider contract |
Nexia\Organization | consume / construct | LegalEntity, OperatingUnit, OrganizationDirectory, OrganizationMemberships |
Nexia\Tenancy | consume | TenantIdentity, TenantRunner, TenantSettings |
Nexia\Events | consume / implement / construct | EventEnvelope, ConsumerResult, EventConsumerRegistration, subscription and recovery modes, EventPublisher, EventDraft, Outbox, InboxConsumer, and App listener contracts |
Nexia\ResourceReference | consume / implement / construct | ResourceRef, safe and protected ResolvedResourceReference values, single and batch resolution, declared-field selection, paged search, effective-range search, Party selection constraints, and availability |
Nexia\ResourceComposition | construct / implement | Strict CompositionSpec and the App-owned authorized-query provider contribution |
Nexia\Laravel\ResourceComposition | construct | AuthorizedCompositionQuery and CompositionQueryContext carry the restored Laravel query, actor and request |
Nexia\ResourceTransfer | implement / construct | Transfer schemas, reference columns, and export source contributions |
Nexia\ResourceImport | consume / implement / construct | App-owned recipe and pipeline contributions, repeatable and temporarily spooled analysis rows, chunked validation, typed decisions, analysis snapshots, queued execution and safe-workbook host contracts, intrinsic definition validation, server-owned DataMigrationStageIdentity, normalized batch state, stages, fill proposals, and disposition totals |
Nexia\Attachments | consume / construct | Stores, authorized readers, directories, targets, safe file/attachment projections, and authorized import-file claims with temporary-copy callbacks |
Nexia\AsyncWork | consume / construct | Actor-owned transient operation status, progress, and results plus atomic identity-bound apply reservations |
Nexia\DataMigration | consume / construct | Stable execution identity, lifecycle status, safe result metadata, and the host recorder contract for durable migration evidence |
Nexia\Templates | implement / construct | CopyableTemplateContribution, application context, source and result value objects |
Nexia\Setup | implement / construct | App-neutral Setup task contribution and evaluator contracts, immutable declaration and assessment DTOs, completion mode, and importance |
Nexia\Mutation | consume / construct | MutationMatcher, lifecycle/action MutationOperation values, and the explicit MutationPublisher host capability |
Nexia\Testing | consume (tests) | Test-only host setup contracts and opaque host-record projections |
Nexia\Http | use constants | Middleware aliases |
Nexia\Laravel\Http | extend / reuse | Controllers\Controller, organization-target request and list-query helpers |
Nexia\Laravel\Http | construct | Laravel list-query adapter for App controllers |
Nexia\Audit | construct | AuditEntry |
Nexia\Signature | consume / implement / construct | Consume SignatureHost and document capabilities; implement data-source/bulk providers; construct request values. Electronic Signature contracts |
Nexia\OfficialSeal | consume | Consume OfficialSealDirectory and OfficialSealUseService; receive seal summaries and assets |
Nexia\Fixture | implement / construct | Implement FixtureContribution; construct fixture keys, references, and context |
Nexia\Guidance | implement / construct | Implement FeatureGuideContribution; construct guide definitions and steps |
Nexia\Resources | construct | Construct ResourceListQuery, ResourceListQuerySchema, and ResourceListSort values |
Nexia\SelfService | consume / implement / construct | Implement action-item and work-context contributions; consume SelfWorkContextRefs |
Nexia\Support | call helper | Call CanonicalPayloadFingerprint for deterministic payload identity |
Output or return: Interfaces versus implementations
Where the platform owns the behavior, the SDK publishes an interface and the
platform binds the implementation. It does ship app-neutral implementations of
its own — the Nexia\Laravel\Models adapter, its model concerns, and the frontend
primitives and hooks — so "contracts only" is true of platform behavior, not of
the whole package. The pattern for platform-owned behavior:
| You depend on | The platform provides |
|---|---|
Nexia\Laravel\Access\Contracts\ResourceAuthorization | Resource row-visibility and action authorization |
Nexia\Laravel\Access\Contracts\TenantPermissionSnapshotAuthorizer | A fail-closed tenant permission snapshot for read-only UI affordances |
Nexia\Identity\Contracts\ActorDirectory | Actor resolution |
Nexia\Identity\Contracts\PartyAccess | Party-backed row visibility |
Nexia\Identity\Contracts\PartyRelationshipDirectory | Actor-filtered Party relationship lookup |
Nexia\Identity\Contracts\PersonPartyProvisioner | Ensure a person Party for an App-owned directory subject from display name and optional email |
Nexia\Identity\Contracts\BatchPersonProfileDirectory / BatchPersonLoginInvitations | Optional bulk profile and login-invitation lookup without per-person host calls |
Nexia\Laravel\Identity\Contracts\PartyDirectory | Party lookup and active-person option search without exposing the Core model |
Nexia\Identity\Contracts\BatchPartyDirectory | Optional bulk active-person lookup by email without exposing the Core model |
Nexia\ResourceReference\Contracts\ReferenceAvailability | Owner lifecycle and type-level authorization state; an optional context must match the supplied actor and Legal Entity |
Nexia\Security\Contracts\RecentAuthentication | A recent-authentication proof for a protected action; it does not replace permission or record authorization |
Nexia\Documents\Contracts\PdfFontProvider | A locale-matched host-bundled local font without exposing Core path construction to the App |
Nexia\Organization\Contracts\OrganizationDirectory | Legal Entity and Operating Unit lookup |
Nexia\Tenancy\Contracts\TenantRunner | Restore one trusted tenant around a callback (runFor, false when absent) or iterate every tenant (runForAll); neither restores an actor |
Nexia\ResourceReference\Contracts\ResourceReferences | Core-governed exact, batch, paged, and effective-range dispatch to owner-authorized providers |
Nexia\ResourceReference\Contracts\ResourceReferenceSelections | Core enforcement for one descriptor-declared browser or write-time selector field, returning availability with an exact result or page |
Nexia\Events\Contracts\EventPublisher | Publish a declared public Event inside the App transaction |
Nexia\Events\Contracts\AppEventListeners / ActorDelegatedAppEventListeners | Tenant-gated App listener registration with explicit subscription and recovery metadata; delegated listeners also restore and reauthorize an actor |
Nexia\Mutation\Contracts\MutationPublisher | After-commit browser wake-up hints for bulk/non-observed Resource changes and successful semantic actions |
Nexia\Attachments\Contracts\AttachmentDirectory | listForTargets() lists authorized attachment summaries for Resource targets |
Nexia\Attachments\Contracts\AuthorizedFileReader | read(filePublicId, legalEntityKey, actor) reads an authorized, isolated, clean file; refusal and absence both return null |
Nexia\Attachments\Contracts\AttachmentStore | Store generated files, attach to Resources, check duplicates and obtain authorized download URLs |
Nexia\Attachments\Contracts\FileLifecycleRecorder | Record bounded file identity and lifecycle metadata after authorization; exclude contents and credentials. |
Nexia\Attachments\Contracts\FileDeliveryAttempts | Persist a delivery attempt before sending bytes and record observed server outcomes; browser receipt is not inferred. |
Nexia\Attachments\Contracts\AuthorizedImportFileReader | Claim an import-only uploadIntentPublicId in Resource/Legal Entity/actor context and read authorized bytes |
Nexia\Attachments\Contracts\AuthorizedImportFileStore | Actor-, Legal Entity-, and Resource-bound import-file claims plus checksum-verified temporary local copies |
Nexia\AsyncWork\Contracts\BackgroundOperationStore | Actor-owned transient operation tickets and bounded progress/results plus an atomic apply reservation that distinguishes an identical retry from conflicting work |
Nexia\ResourceImport\Analysis\Contracts\ImportAnalysisStore | Resource/scope/actor-authorized access to short-lived encrypted parser results cached by Media checksum, analysis kind, and parser version |
Nexia\ResourceImport\Execution\Contracts\QueuedImportExecution | Restored tenant, actor, organization scope, and request context in which queued import work performs its point-in-time authorization checks |
Nexia\ResourceImport\Workbook\Contracts\SafeWorkbookInspector | Host archive/XML safety inspection before an App parser opens an untrusted workbook |
Nexia\DataMigration\Contracts\DataMigrationRunRecorder | Durable server-owned start, clean completion, partial completion, and failure evidence without exposing the host model |
Nexia\Process\Contracts\ProcessRuntime | Event-start availability, read-only instance lookup, and durable exact activity correlation with ProcessMessageDelivery / ProcessMessageIdentity |
Nexia\AppDescriptors\Contracts\ResourceSummaries | Resource summary provider discovery and dispatch |
Nexia\Testing\Contracts\* | Test-environment fixture and inspection surfaces without host models |
Resolve directly bound host contracts from the container. The optional batch contracts above have no separate binding: inject Nexia\Identity\Contracts\PersonProfileDirectory, Nexia\Identity\Contracts\PersonLoginInvitations or Nexia\Laravel\Identity\Contracts\PartyDirectory, then check that the returned implementation implements its corresponding batch interface. Current Core implementations support all three batch capabilities. Never type-hint a platform class or use its Eloquent model as an App API.
Nexia\Tenancy\Contracts\TenantIdentity, Nexia\Identity\Contracts\Actor and Nexia\Identity\Contracts\Party are identities supplied through context or call results, not services to assume independently injectable. Contributions and App providers are implemented by the App; test-host bindings are configured only in the test environment.
The batch profile and Party directories query only the tenant context already
restored by the caller; their keys and emails are not cross-tenant selectors.
Batch login-invitation status additionally requires the current Actor and a
Legal Entity key, and reports NotAuthorized when that actor may not invite.
PersonPartyProvisioner::ensureForDirectorySubject($displayName, $email) returns
the app-neutral Party contract. The host reuses the single unambiguous active
person Party for the email, including one already linked to a user; otherwise it
creates a person Party. The App receives no host model and must not reimplement
email-based identity convergence itself.
RecentAuthentication::isFresh($actor, $withinMinutes) is an additional proof
for a protected action. Keep the operation's permission, row, scope, and state
checks in place. PdfFontProvider::forLocale() returns a host-owned local
PdfFontAsset when an App needs to embed a font in a generated PDF; the App
must not reconstruct Core filesystem paths.
PartyDirectory::searchActivePeople($search, $limit, $excludedKeys) returns
PersonDirectoryEntry options with an opaque key, public id, display label, and
optional contact email.
ResolvedResourceReference keeps public fields separate from
protectedValues(). Protected values are accepted only from an exact
resolve() or resolveMany() call whose context explicitly sets
includeProtectedValues; searches reject them, and snapshots, debug output,
and PHP serialization omit them. People uses this boundary to return bank
account values for the exact payroll.bank-export purpose without making a
People-specific payment contract part of the SDK. The same owner can add a safe
effective planned-schedule projection for the
workforce.effective-planned-schedule purpose, bounded by asOf and
effectiveThrough. Actorless consumers use a public Event and local projection
instead of fabricating an actor.
Tenant permission snapshots
TenantPermissionSnapshotAuthorizer evaluates several tenant-scoped permission
keys with one app-neutral contract:
Here $user is the authenticated host user already supplied to the request. These two tenant-scoped Core permissions demonstrate the shape; pass the tenant-scoped permissions needed by your UI.
use Nexia\Laravel\Access\Contracts\TenantPermissionSnapshotAuthorizer;
$snapshot = app(TenantPermissionSnapshotAuthorizer::class)->forPermissions($user, [
'system.apps.read',
'system.data.export',
]);The result contains every requested string key verbatim. Unknown or retired
permissions, permissions from a non-operational App, non-tenant permissions,
and subjects that cannot be resolved all evaluate to false; non-string input
is ignored. The contract uses the ambient tenant and deliberately accepts no
Legal Entity or Operating Unit argument. Use the snapshot to render read-only
affordances such as menus or buttons. A write still performs point-in-time
authorization in its request path.
AttachmentDirectory::listForTargets() accepts AttachmentTarget values and returns attachment summaries visible to the actor; it does not read bytes. AuthorizedFileReader::read() takes a filePublicId. In the restored tenant and request Legal Entity context, it checks actor authorization, isolation and clean status; absence, refusal, boundary mismatch and an unclean file return null.
AttachmentStore::createGeneratedFile() stores a generated file and attach() links it to a Resource target. exists() checks an attachment public id against the target; fileAttached() checks whether a file public id is already linked. These checks are not uniqueness constraints or domain mutation authorization. The App must authorize its business write and handle duplicates before storing/attaching. downloadHrefIfAuthorized() returns a URL only when the actor may read it.
For import-only uploads, AuthorizedImportFileReader::read(uploadIntentPublicId, resourceKey, legalEntityKey, actor) claims and reads the upload. Use AuthorizedImportFileStore::claim(), find() and withLocalCopy() for metadata reauthorization or temporary-copy processing of larger files. Neither contract is the general Resource attachment directory.
Party relationship and Resource Summary lookups likewise return no projection when the actor-visible provider cannot answer; callers do not receive a host model or a reason that widens disclosure.
There is no kernel Nexia\Models namespace or host-model facade. Use the focused Identity,
Organization, Party, or Installed Apps contract; production Apps never query or
receive a host Eloquent model. Nexia\Testing\Contracts\HostTestStore is test-only.
Mutation identity and publication
MutationMatcher is an app-neutral recheck condition. It names either
Resource Catalog keys with lifecycle operations (Created, Updated,
Deleted, Restored) or semantic action keys with only Succeeded. It cannot
mix the two subject types. Keys and operations inside one matcher use OR
semantics; optional resourceIds narrow a Resource matcher. Resource keys,
action keys, and Resource IDs are each limited to 160 UTF-8 bytes across the
SDK, host header, Gateway, and Shell boundaries.
The host automatically observes committed Eloquent lifecycle events for every
model bound through ResourceCatalogContribution. Ordinary generated Resource
controllers therefore add no save/delete instrumentation. Inject
MutationPublisher only when the write bypasses model events, such as a bulk
query update, or when a successful semantic action has no Resource Catalog
root:
use Nexia\Mutation\Contracts\MutationPublisher;
use Nexia\Mutation\MutationOperation;
$mutations = app(MutationPublisher::class);
$mutations->resourceChanged(
'workshop.note',
MutationOperation::Updated,
$publicId,
);
$mutations->actionSucceeded('workshop.note.archived');Call the publisher only after the business write succeeds. The host derives tenant, actor, initiator, request, surface, and correlation context from its trusted runtime; App code supplies none of them. Publication occurs after the current transaction commits, or immediately when no transaction is active.
This signal is a same-browser wake-up hint, not a durable domain event, audit
record, cache invalidation, or completion result. A consumer always re-reads
authoritative state. Use Nexia\Events when another App or runtime must react
reliably.
Cross-App access
App-to-App imports and package dependencies are forbidden. There is no exception. Three supported alternatives:
| Need | Mechanism |
|---|---|
| Read another App's record | Nexia\ResourceReference — resolution and search contributions |
| React to another App's change | Nexia\Events — consume a cataloged public Event through an explicit replay, reconcile, or ephemeral recovery contract; only delegated listeners restore the user and recheck authority |
| Share a capability | Publish an app-neutral SDK contract and bind it in the platform |
A ResourceRef names another App's record without importing its model. Core
resolves it, applies the actor's authority, and returns only what they may see.
The canonical browser selector also requires the consuming Resource and field,
then enforces its declared targets, purpose, permissions, and owner availability
before dispatch.
For a manually built Dashboard that reads across two Resources, Core composes
the existing Resource contracts instead of asking either App to import the
other. The SDK publishes only the app-neutral CompositionSpec wire DTO and
matching PHP/TypeScript validators. The spec names semantic Resource keys,
source aliases, a server-issued relationship key, fields, bounded filters,
dimensions, measures, and result shape. It has no table, model, SQL, arbitrary
expression, planner, or executor surface. Core resolves the keys against the
live actor-filtered catalog and repeats authorization when it plans and runs the
query. This is a manual human Dashboard capability, not an App or Agent
authoring interface.
When a surface is missing
Check the installed package and host version before adding an API. A source checkout is only needed when you are changing that package. Keep App-specific behavior local and use Resource contracts or published events for cross-App integration. Request a platform capability when the existing public APIs cannot express the task; consume it once both SDK and host support it.
Do not import Core classes, another App, or edit vendor/. To add an available
SDK checkout to an existing local setup, use task apps:add PACKAGES=app-sdk.
Capabilities without an App-side contract today
| Capability | Host-only class |
|---|---|
| Site Configuration | App\Settings\SiteConfigRegistry |
| Installation initializers | App\AppRuntime\TenantAppInitializerContribution |
Neither class is an SDK API. Consider Site Configuration as a separate SDK candidate only after an actual App needs to contribute declarative items to the shared settings screen. For an App-owned settings screen, follow Add a settings page. First evaluate installation needs against tenant migrations and Required installation content, optional Copyable templates, and Contribute Setup tasks for operator readiness.
Existing package imports of host-only surfaces, when any remain, appear as
explicit exceptions in .nexia/coupling-baseline.json. A clean App has no
file; the ratchet permits only recorded file-and-symbol pairs and rejects new
ones.
Errors
| Message | Cause | Resolution |
|---|---|---|
| Coupling ratchet rejects a new file-and-symbol pair | A new App\* reference from a package | Import from Nexia\*, check the installed SDK capability |
Class "Nexia\..." not found | The installed SDK does not export the symbol | Check the namespace and installed package version; use a compatible SDK and host release |
Target [Nexia\...\Contracts\X] is not instantiable. | The platform has no binding for that contract | The contract exists but is unbound — the host binding is missing |
Module not found: @/... or @shell/... in a package | Frontend platform alias import | Import from @nexia/sdk |
Use in your App
Use the installed SDK namespace and the host binding together. For a first Resource, follow Create a Resource; the generated files show the model, authorization, and contribution APIs in context.
Related
- HTTP middleware — App route stacks, every registered alias, parameters, ordering, and refusal codes
- React components and hooks — the frontend symbols an App reaches for most; use
dist/index.d.tsfor pure contracts anddist/host.d.tsfor host-bound APIs - App SDK — why the boundary is shaped this way
- Contribution contracts — every contribution interface and its method
- Fix boundary violations — fixing a rejected import
Use FileDeliveryAttempts::callback(attemptId, producer) around the actual byte producer after prepare(). It records server start and terminal outcomes; a constructed response does not establish delivery. FileLifecycleRecorder accepts bounded identity/context metadata only and does not grant authorization. Inventory observations and retention-pending facts do not claim historical downloads or deleted bytes.
AuthorizedImportFileStore::receiveLocal() routes existing multipart input through verified host intake. discardUnbound() is for a newly claimed input that your App has not bound to a business record (for example, a rejected duplicate upload); do not pass accepted evidence. Custom implementations must provide both methods and delivery callbacks when adopting this SDK change.