Skip to content
Concept

Search Configuration Profiles

Which Search settings are generic deployment choices, which exist only for Typesense, and how the database and Typesense profiles differ.

Search Configuration Profiles

Ordinary App development can use the database profile below. Enable the local Typesense profile only when exercising index behavior, then reindex and check drift. Keep engine credentials with the deployment owner; your App continues to use the same Resource contracts. Matching index counts confirms index state, not actor access.

Start with the database profile

The database profile needs no external engine:

SCOUT_DRIVER=database
SCOUT_QUEUE=false
SCOUT_AFTER_COMMIT=true
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED=false
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE=none

Because SEARCH_CANDIDATE_GATEWAY is absent, Nexia selects the database gateway. This is the self-contained local and on-premise profile. It does not provide Typesense scoring, typo tolerance, facets, or highlights.

What it is

Search configuration is split into a generic Nexia selection and optional engine-specific connection details. The generic selection tells Core which index integration and candidate gateway to use. Typesense variables exist only when the deployment selects Typesense. A database-only deployment should not need placeholder Typesense credentials.

The settings have distinct jobs:

SettingScopeMeaning
SCOUT_DRIVERGenericSelects Laravel Scout's indexing engine
SEARCH_CANDIDATE_GATEWAYGeneric, optionalOverrides the candidate supplier; when omitted, Nexia follows Scout (typesense or database)
SCOUT_QUEUEGenericChooses whether Scout synchronization is queued
SCOUT_AFTER_COMMITGenericDelays index sync until a database transaction commits
KNOWLEDGE_SEARCH_SEMANTIC_ENABLEDKnowledge-onlyEnables semantic candidates for governed Knowledge retrieval; false by default
KNOWLEDGE_SEARCH_EMBEDDING_PROFILEKnowledge-onlySelects an immutable provider/model/version/dimension/storage generation; none by default

config/search.php owns the candidate selection rule:

Code example
PHP
'gateway' => env(
    'SEARCH_CANDIDATE_GATEWAY',
    env('SCOUT_DRIVER') === 'typesense' ? 'typesense' : 'database',
),

Leaving the override unset is the normal configuration. Set it explicitly only for a staged migration or diagnostic profile where indexing and candidate retrieval deliberately use different adapters.

How it fits into deployment profiles

A Typesense profile changes the generic selection and supplies the following engine-owned values through its deployment or secret-management system:

Typesense settingRequired role
TYPESENSE_HOSTEngine host name; config/scout.php defaults to the Compose service name typesense
TYPESENSE_PORTEngine port; default 8108
TYPESENSE_PATHOptional URL path; default empty
TYPESENSE_PROTOCOLhttp or https according to the deployment
TYPESENSE_API_KEYServer-only administrative key used by Scout and index lifecycle operations
TYPESENSE_SCOPED_KEY_PARENTA real search-only key created in Typesense and used to sign short-lived tenant-scoped child keys

The two keys are not interchangeable. TYPESENSE_API_KEY can perform index administration and never goes to a browser. TYPESENSE_SCOPED_KEY_PARENT must refer to a key that exists in Typesense with search-only actions. An arbitrary string with that environment-variable name is not a valid parent. Nexia's server-side Typesense adapter signs a tenant-filtered child key from it and uses that child for candidate queries.

Index lifecycle

Only allowlisted Resource keys use Typesense; every other list keeps Scout's database search. A shared collection separates tenants by tenant_key. nexia-search:reindex builds and backfills a new collection, then swaps the stable alias only after every ready tenant succeeds; the previous collection remains for rollback. --tenant=<key> refreshes that tenant inside the live collection without swapping the alias. If no alias exists yet, the command reports a full rebuild across ready tenants instead. nexia-search:check-drift reports count differences without repairing them. Database deployments need neither command.

Knowledge semantic generations

Knowledge chunks have a separate lifecycle. A profile fixes provider, model/version, dimension, extraction contract, and storage shape; changing a model name is not an in-place upgrade.

After enabling a valid profile, explicitly queue a rebuild with:

Code example
Shell
task artisan -- nexia-search:reconcile-knowledge-search-index --semantic-reindex --limit=500

The command queues eligible revisions per ready tenant; scheduled reconciliation drains the remainder. Indexing validates a whole batch and rechecks publication eligibility before replacing a generation. The only implemented profile is deterministic-local-test-vector-16-v1, for local/testing use. Keep semantic search disabled in production until a production profile is available.

Local Typesense profile

The local stack is a Compose service, never Typesense Cloud. Point a local deployment at Cloud and a reindex rewrites production collections.

SCOUT_DRIVER=typesense
SEARCH_CANDIDATE_GATEWAY=typesense
SCOUT_QUEUE=false
TYPESENSE_HOST=typesense
TYPESENSE_PORT=8108
TYPESENSE_PROTOCOL=http

Start it with task dev:up:typesense. It starts app, vite, and typesense (and configured Reverb), not Horizon, so local SCOUT_QUEUE stays false. The task registers a real search-only parent key for local HTTP; use the following fallback only when that registration is unavailable:

Code example
Shell
curl -X POST 'http://localhost:8108/keys' \
  -H 'X-TYPESENSE-API-KEY: local-typesense-search-key' \
  -H 'Content-Type: application/json' \
  -d '{"description":"local search-only parent","actions":["documents:search"],"collections":["*"],"value":"local-typesense-scoped-key-parent"}'

Use the returned value for TYPESENSE_SCOPED_KEY_PARENT; the admin key is never a valid parent.

Boundaries

Do not put either Typesense key in VITE_* variables. Laravel only exposes selected public frontend configuration, and Search credentials are not public.

Do not copy example secrets from a repository or use the Docker development bootstrap key as a production credential. The production secret store and engine key administration are the owners of those values.

Do not use SEARCH_CANDIDATE_GATEWAY to bypass authorization. It selects a supplier of untrusted IDs. CoreSearchService still reads the tenant database and applies current permission and record scope.

Worked example

A deployment that has already provisioned Typesense connection values, an administrative key, and a search-only scoped-key parent selects the engine with:

SCOUT_DRIVER=typesense
SEARCH_CANDIDATE_GATEWAY=typesense
SCOUT_QUEUE=true
SCOUT_AFTER_COMMIT=true
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED=false
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE=none

After rebuilding cached configuration and restarting long-lived workers, the operator backfills one allowlisted Resource across every tenant, then verifies one tenant:

Code example
Shell
task artisan -- nexia-search:reindex shell.workspace
task artisan -- nexia-search:check-drift --tenant=acme

Successful output reports one backfilled into row per ready tenant, a single stable alias swap for the Resource, and then OK rows whose database and engine counts match. Adding --tenant=acme to the reindex refreshes that tenant alone and leaves the alias where it is. This verifies index state, not user authorization; an API search still revalidates every returned record.

Source of truth: docs/developers/content/en/operations/search-configuration.md