Indexing with the headless prerenderer
March 24, 2026 · View on GitHub
Cards are indexed by rendering them and deriving search documents from the rendered output. Indexing goes through the headless prerenderer, which runs a pool of Chromium pages (via Puppeteer) and renders card routes in the built host app.
Components involved
Worker(packages/runtime-common/worker.ts): pulls indexing jobs from the queue and registers the indexing tasks.- Indexing tasks (
packages/runtime-common/tasks/indexer.ts): implement from-scratch and incremental jobs and create anIndexRunner. IndexRunner(packages/runtime-common/index-runner.ts): walks realm contents, asks the prerenderer to render cards/modules, records dependencies, and writes index entries withIndexWriter.- Prerenderer service (
packages/realm-server/prerender): HTTP API that drives headless Chrome through Puppeteer. It maintains a page pool and renders HTML for cards/modules. - Remote prerenderer client (
packages/realm-server/prerender/remote-prerenderer.ts): wraps the prerenderer HTTP API with retry/backoff logic for workers and the realm server.
High-level flow
sequenceDiagram participant Queue participant Worker participant IndexRunner participant Prerenderer participant Chromium participant HostApp Queue->>Worker: reserve from-scratch / incremental job Worker->>IndexRunner: construct with reader, indexWriter, prerenderer IndexRunner->>Prerenderer: POST /prerender-card (realm, url, permissions, options) Prerenderer->>Chromium: render host app route via Puppeteer Chromium->>HostApp: load built host app + card route Chromium-->>Prerenderer: captured HTML + meta Prerenderer-->>IndexRunner: RenderResponse / ModuleRenderResponse IndexRunner->>IndexWriter: write index entries (html, search doc, deps) IndexRunner-->>Worker: stats + ignoreData Worker-->>Queue: complete job
From-scratch indexing
- A job is queued to index an entire realm (usually at startup or after migration).
- The worker task builds an
IndexRunnerwith:- Authenticated fetch and a
Readerfor the realm IndexWriterconnected to the realm’s DB adapter- Permissions and user context for the realm user
- The remote prerenderer client
- Authenticated fetch and a
IndexRunner.fromScratchwalks realm files, renders each card/module through the prerenderer, collects dependencies, and writes entries into a new batch.- Results include
statsandignoreData(mtime hashes) to speed later incremental runs.
Incremental indexing
- A job is queued with the changed URLs and operation (
updateordelete). - The worker creates an
IndexRunnerwith the previousignoreDataand runsIndexRunner.incremental. - Incremental runs:
- Invalidates existing index entries that depended on the changed URLs.
- Re-renders the affected cards/modules via the prerenderer.
- Writes updated entries and returns
stats,invalidations, and newignoreData.
What the prerenderer does
- Runs a dedicated HTTP service (see
packages/realm-server/prerender/prerender-app.ts). - Accepts
/prerender-cardand/prerender-modulerequests containing:realm: fully qualified realm URLurl: card/module URL to renderuserIdandpermissions: determine auth context for the render- Optional render options (e.g., cache clearing)
- Uses Puppeteer to:
- Reuse or create a Chromium page from its pool
- Navigate to the host app’s render route for the requested card/module
- Wait for render completion and capture HTML plus render metadata
- Returns a JSON:API-style payload consumed by
IndexRunner. - Employs retry/backoff in the client (
remote-prerenderer) when the service is draining or temporarily unavailable.
Artifacts stored in the index
For each card instance we persist:
- Captured HTML for the requested render mode
- Search document (includes evaluated computed fields)
- Dependency graph (instances and modules referenced during render)
- JSON:API resource for the instance
- Adoption chain (types the card derives from)
Operational notes
- The prerenderer URL is required for workers/realm-server; missing or unreachable URLs fail fast.
FROM_SCRATCH_JOB_TIMEOUT_SECcontrols the from-scratch indexing job timeout (seconds) and caps the queue worker runtime; default is 3600.- For local testing helpers, see
packages/host/tests/helpers/index.gtsandpackages/realm-server/tests/helpers/index.tswhich start a test prerenderer and wire the same flow.