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:
| Setting | Scope | Meaning |
|---|---|---|
SCOUT_DRIVER | Generic | Selects Laravel Scout's indexing engine |
SEARCH_CANDIDATE_GATEWAY | Generic, optional | Overrides the candidate supplier; when omitted, Nexia follows Scout (typesense or database) |
SCOUT_QUEUE | Generic | Chooses whether Scout synchronization is queued |
SCOUT_AFTER_COMMIT | Generic | Delays index sync until a database transaction commits |
KNOWLEDGE_SEARCH_SEMANTIC_ENABLED | Knowledge-only | Enables semantic candidates for governed Knowledge retrieval; false by default |
KNOWLEDGE_SEARCH_EMBEDDING_PROFILE | Knowledge-only | Selects an immutable provider/model/version/dimension/storage generation; none by default |
config/search.php owns the candidate selection rule:
'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 setting | Required role |
|---|---|
TYPESENSE_HOST | Engine host name; config/scout.php defaults to the Compose service name typesense |
TYPESENSE_PORT | Engine port; default 8108 |
TYPESENSE_PATH | Optional URL path; default empty |
TYPESENSE_PROTOCOL | http or https according to the deployment |
TYPESENSE_API_KEY | Server-only administrative key used by Scout and index lifecycle operations |
TYPESENSE_SCOPED_KEY_PARENT | A 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:
task artisan -- nexia-search:reconcile-knowledge-search-index --semantic-reindex --limit=500The 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:
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:
task artisan -- nexia-search:reindex shell.workspace
task artisan -- nexia-search:check-drift --tenant=acmeSuccessful 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.
Related
- Environment Variables — looks up every Search and embedding deployment value by owner.
- Governed Search Runtime — traces candidate IDs through Core authorization.
- Configuration Ownership — decides whether a value belongs to deployment, platform, tenant, or App state.
- Octane·FrankenPHP Runtime — explains why cached configuration changes require worker restart.
- Artisan commands — provides the exact signature of other supported commands.