Run a standalone search Worker
The standalone Worker in worker/ is a search server: it serves the Elasticsearch routes at its root, and any Elasticsearch client, curl or another Worker can call it over HTTP. Use it when several applications share one index, or when you want search behind its own URL and credentials.
It is the Worker the Quick start runs.
What is in it
Section titled “What is in it”worker/src/index.ts is short. It creates the engine once per isolate, reads the index from its static assets, turns on diagnostics headers and passes every request to the engine:
import wasm from "@ferroseek/engine/wasm";import { assetsIndexSource, authFromEnv, createFerroseek, type Ferroseek } from "@ferroseek/engine";
let search: Ferroseek | undefined;
function getSearch(env: Env): Ferroseek { search ??= createFerroseek({ wasm, source: assetsIndexSource(env.ASSETS, "index"), auth: authFromEnv(env as unknown as Record<string, unknown>), diagnostics: true, onError: (error, phase) => console.error(`ferroseek ${phase} failure`, error), }); return search;}
export default { async fetch(request: Request, env: Env): Promise<Response> { return getSearch(env).fetch(request); },} satisfies ExportedHandler<Env>;worker/wrangler.jsonc binds worker/assets/ as ASSETS with run_worker_first: true, so every path goes to the Worker and the index files under assets/index/ are never downloadable. The dev server port, 4090, is set there too.
Run it locally
Section titled “Run it locally”npm run index:build:100k # or index:build:2knpm run devnpm run dev rebuilds the WebAssembly engine and starts wrangler dev --config worker/wrangler.jsonc on http://localhost:4090.
Turn on authentication
Section titled “Turn on authentication”Without credentials configured, the Worker answers anyone. To require credentials, set them as Worker secrets. Ferroseek accepts the same two schemes as Elasticsearch:
| Secret | Scheme |
|---|---|
FERROSEEK_USERNAME and FERROSEEK_PASSWORD |
Authorization: Basic base64(user:password) |
FERROSEEK_API_KEY |
Authorization: ApiKey base64(id:api_key) |
You can set either pair, or both. FERROSEEK_API_KEY is the encoded value Elasticsearch returns when it creates an API key: base64 of id:api_key. Setting only one of username and password, an empty value, or an API key that does not decode to id:api_key is a configuration error: the Worker then answers every request with 500 settings_exception naming the problem, instead of running unprotected.
For local development, put the values in worker/.dev.vars, which wrangler dev reads:
FERROSEEK_USERNAME=shopFERROSEEK_PASSWORD=change-meIn production, store them as secrets:
npx wrangler secret put FERROSEEK_USERNAME --config worker/wrangler.jsoncnpx wrangler secret put FERROSEEK_PASSWORD --config worker/wrangler.jsoncThen send credentials with every request:
curl -s -u shop:change-me http://localhost:4090/products/_countWhat a failed login looks like
Section titled “What a failed login looks like”A missing or wrong credential gets the same answer a real Elasticsearch 7.17 with security enabled gives: 401 with a security_exception body and the WWW-Authenticate challenge headers (Basic realm="security" charset="UTF-8", plus ApiKey when an API key is configured). The response shapes were captured from a real 7.17.4 server.
Credentials are compared in constant time: both sides are hashed with SHA-256 before the comparison, and the username and password checks always both run, so response timing does not reveal which part was wrong or how long the secret is.
Call it from an application
Section titled “Call it from an application”Point any Elasticsearch client at the Worker’s URL. With the client package, that is the SEARCH_URL environment variable plus the credentials:
SEARCH_URL=https://search.example.comSEARCH_USERNAME=shopSEARCH_PASSWORD=change-meDiagnostics headers
Section titled “Diagnostics headers”The standalone Worker creates the engine with diagnostics: true, so responses carry x-engine-time-us, x-wasm-memory-bytes, and x-index-load-ms on the request that loaded the index. If you do not want to expose them, set diagnostics to false in worker/src/index.ts.