Skip to content
ferroseekdocumentation homemain, packages at version v0.1.0
Ferroseek on GitHub

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.

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:

worker/src/index.ts (abridged)
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.

npm run index:build:100k # or index:build:2k
npm run dev

npm run dev rebuilds the WebAssembly engine and starts wrangler dev --config worker/wrangler.jsonc on http://localhost:4090.

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:

worker/.dev.vars
FERROSEEK_USERNAME=shop
FERROSEEK_PASSWORD=change-me

In production, store them as secrets:

npx wrangler secret put FERROSEEK_USERNAME --config worker/wrangler.jsonc
npx wrangler secret put FERROSEEK_PASSWORD --config worker/wrangler.jsonc

Then send credentials with every request:

curl -s -u shop:change-me http://localhost:4090/products/_count

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.

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.com
SEARCH_USERNAME=shop
SEARCH_PASSWORD=change-me

See Connect with the client.

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.