Skip to content
Guide

Command Palette

Contribute searchable destinations and commands while governed Resource Search supplies authorized record results.

Make an App screen discoverable with Cmd+K. A searchable Add a menu destination already supplies this behavior; it needs no second registration.

Publish and find the destination

Set searchable: true on the existing navigation entry (the default), use a translated labelKey, and retain its read permission. Refresh discovery after adding a contribution class, then open the palette with an authorized actor and search the localized screen name.

Confirm the result

Selecting the result opens the registered App screen. An actor without its permission does not discover it, and direct API access remains protected. searchable: false suppresses search without granting or revoking authority.

For record results, publish ordinary Resource metadata and searchable model fields through Create a Resource; Core performs governed candidate retrieval and reauthorization. Use SDK PaletteCommandContribution only for an executable command, and the locate helper below for agent navigation.

Available contribution paths

CapabilityAvailable to an App PackageMechanism
Destination searchYessearchable on a navigation entry
Record search (find Order #123)Through a published ResourceCore governed Search over eligible Resource models
Executable commandsYesSDK PaletteCommandContribution
Agent locate actionsYesShellNavigationLocateAction

Import executable-command contracts from Nexia\Palette. App\Palette\* remains a Core implementation namespace and is not an App Package boundary. There is no App-owned Palette record-search contribution.

Record hits come from CoreSearchService, the same governed Search runtime used by the expanded Search surface. It selects eligible Resource models, asks the configured candidate gateway only for identifiers, then reloads those records from the tenant database and applies current visibility and Policy checks. An App's part is to publish its normal Resource contract and searchable model fields. Core owns eligibility, App installation checks, and final authorization.

NexiaEntityModel already uses UsesFilterableScoutSearch; fields marked searchable: true in resourceListFields() are the source for its Search projection. External indexing remains fail-closed behind Core's Resource allowlist. Do not create a second Palette-specific query or return display data directly from an external engine.

Use PaletteCommand::make() for command payloads. Do not declare an App Family, App key, or hierarchy on individual commands; Core derives ownership from the registered contribution location.

Result grouping

Core returns an explicit placement on every navigation, command, and entity result. Clients consume that field directly rather than inferring ownership from an id, route, context, or the current result set.

Result originRendered as
App-ownedApp Family → App → destination, command, or record
HostLocalized Platform categories

Your App contributes app_family membership and in-family launcher_order in composer.json. Core contributes the Family label and Family order through AppFamilyCatalog, shared with the App Launcher and App Catalog. The App's overview destination is identified by overview_navigation_id, so it remains first even when a search result contains only part of the App catalog.

Agent locate actions

A locate action lets an agent navigate to a list surface and pre-apply search, filters, and sort. In an existing NavigationContribution, use Nexia\Navigation\Concerns\ContributesNavigationDestinationActions and import Nexia\Navigation\ShellNavigationLocateAction. The following method excerpt belongs inside that class; the trait exposes it as agentNavigationActions():

Code example
PHP
protected static function agentNavigationActionDefinitions(): array
{
    return [
        ShellNavigationLocateAction::make(
            action: 'assets.asset.locate',
            url: '/apps/assets/assets',
            permission: 'assets.asset.read',
        ),
    ];
}

The helper supplies these defaults; the App still owns the correct route and permission:

FieldFixed value
intentlocate
effectread_only
requires_user_submitfalse
input_schemaq (free text), filters (declared keys only), sort (prefix - for descending)

The action carries label_key and description_key built from the agent-navigation i18n prefix — catalog keys, not copy.

It is read-only by construction. A locate action cannot mutate anything, and the permission still gates it. filters accepts declared filter keys only — read the registered locate action schema for the exact keys and option values.

Boundaries

searchable is indexing, not authorization. A palette result still respects the destination's permission gate; an actor without it never sees the entry. Setting searchable: false is a noise decision, not a security control.

Only permission-visible entries are indexed. The palette indexes the filtered stream, so it cannot reveal the existence of a destination an actor may not use.

Destination labels come from the locale catalog. A hard-coded label is what the operator searches against in every locale — pass an exact catalog key as labelKey.

Workbench hierarchy stays flat so the backend can permission-filter every child independently and the palette can index every destination. Do not model depth you do not need.

The locate-action excerpt follows packages/assets/src/Contribution/Resources/AssetModule.php. Exact executable command fields are defined in packages/app-sdk/packages/laravel/src/Palette/PaletteCommand.php.

Source of truth: docs/developers/content/en/platform-extensions/command-palette.md