Crux
GuidesRuntime Engine

Convex Runtime

Declare Convex as a host-bound Runtime Engine and run durable work inside Convex functions.

Use convex() when Convex should own runtime state, scheduler wake, and execution boundaries.

Configure

import { config } from '@use-crux/core'
import { convex } from '@use-crux/convex/runtime'

export default config({
  runtime: convex({
    retention: {
      terminalWork: '7d',
      terminalSnapshots: '30d',
    },
  }),
})

convex() is a declaration, not an executable Node runtime. Runtime-backed APIs must run inside generated or hand-written Convex functions; outside that host boundary Crux throws RUNTIME_HOST_ONLY.

Runtime handlers

Generated Convex runtime files wire component refs without importing target modules into isolate handlers:

convex/_crux/generated.ts
import { makeFunctionReference } from 'convex/server'
import { createConvexRuntimeHandlers } from '@use-crux/convex/runtime'
import { components } from '../_generated/api'

const targetExecutor = makeFunctionReference<'action', { envelope: unknown }, unknown>('_crux/targets:executeTarget')

export const { handleWake, deliverSignal, resumeFlow, runTask, fireTimer } = createConvexRuntimeHandlers({
  component: components.crux,
  targetExecutor,
})
convex/_crux/targets.ts
'use node'

import { createConvexRuntimeTargetExecutor } from '@use-crux/convex/runtime/node'
import { components } from '../_generated/api'
import { reviewFlow } from '../../src/review'
import { embedDocument } from '../../src/embed'

export const { executeTarget } = createConvexRuntimeTargetExecutor({
  component: components.crux,
  targets: [reviewFlow, embedDocument],
})

Convex scheduler calls these handlers for wake, signal, resume, task, and timer delivery. App-facing APIs stay in your own Convex functions.

The generated component includes runtime.composites.run. The Convex store adapter routes each named Runtime Engine composite through that single mutation, so task enqueue, event delivery, wake completion, retry/failure, cancellation, and maintenance commits are atomic in Convex while still using the core kernel's shared composite bodies.

Convex maintenance queries are namespace-scoped and bounded. The component stores work ids on outbox rows for targeted orphan recovery, and event append uses opaque event cursors rather than a shared namespace counter.

When app code starts runtime-backed work directly from a Convex action, wrap the call in createCruxConvex(...).run(ctx, target, fn). The profile binds the current Convex ctx, component refs, and scheduler before core starts the durable flow.

Rules

  • Import targets statically; generated files should not rely on dynamic import.
  • Use top-level named exports and literal target names.
  • Do not call runtime-backed APIs from a Next server process configured with runtime: convex().
  • Configure retention on convex({ retention }); the component prunes terminal runtime rows during host-bound maintenance.
  • Convex runtime execution is fencing-only: stale final commits are rejected with LEASE_LOST, and Convex host bindings pass leaseExtension: false so isolate and Node action paths do not run in-process heartbeat timers. Keep target work below the configured lease TTL or increase leaseTtlMs for long actions.
  • Use Convex setup/deploy flow for component tables; Crux setup commands report host-specific diagnostics rather than creating paid resources.

On this page