Crux
API Reference@use-crux/core

Config

Full crux.config.ts reference for project policy, local tooling, runtime hooks, persistence, observability, plugins, and experimental indexer options.

config() is the single public API for Crux project configuration. Use it for policy, local tooling, runtime hooks, persistence, observability, plugins, and explicit experimental opt-ins.

Do not repeat prompts, contexts, tools, agents, flows, registries, or skills in config(). Author those values in normal TypeScript modules and export them. Local tooling discovers them from source.

crux.config.ts
import { config } from '@use-crux/core'

export default config({
  quality: {
    defaults: { replay: 'record-new' },
  },
  lint: {
    profile: 'recommended',
  },
  generation: {
    autoEscape: true,
  },
})

An empty config is valid:

crux.config.ts
import { config } from '@use-crux/core'

export default config({})

Behavior

config() applies immediately when the module is imported. Node module caching means a normal crux.config.ts module runs once per process.

When local tooling imports the config with CRUX_INDEX=1, Crux returns an inert config object: no observability transport, Runtime Bridge, store, middleware, tokenizer, or plugin side effects are installed. This is what crux config inspect uses.

Outside index mode, config application is transactional: persistence and explicit observability are installed before user plugins, the Runtime Bridge connects after the prompt registry exists, and dispose() tears down the bridge before restoring config-owned hooks, plugins, and observability. This keeps plugin setup predictable without adding another public setup API.

config() is one-config-per-process. Re-running it replaces the previous active installation before applying the new one, so hot reload does not stack middleware or duplicate plugin hook fan-out. Disposing an older returned Crux object is safe after a later config() call; it cannot clear the new active installation. Per-request or multi-tenant config scoping is intentionally out of scope for this process-global API.

devtools and observability have different ownership:

  • devtools.serverUrl is the local development fallback. When no explicit observability transport is configured, Crux installs the devtools transport before user plugins.
  • observability.enabled: false, observability.transport, or observability.serverUrl makes the observability domain own the transport. In that case devtools does not install its fallback transport, and user plugins see the explicit observability state.

Shape

interface CruxConfig {
  quality?: QualityConfig
  lint?: CruxLintConfig
  indexer?: CruxIndexerConfig
  experimental?: CruxExperimentalConfig
  runtime?: RuntimeEngineDefinition
  persistence?: CruxPersistenceConfig
  generation?: CruxGenerationConfig
  devtools?: CruxDevtoolsConfig
  observability?: CruxObservabilityConfig
  plugins?: readonly CruxPlugin[]
}
OptionTypeDefaultConsumed by
qualityQualityConfigzero-config Quality defaultscrux quality, crux dev, Quality views
lintCruxLintConfig{ profile: 'recommended' }Project Index lint, crux lint, devtools
indexerCruxIndexerConfigno third-party extensions@use-crux/indexer
experimentalCruxExperimentalConfigno experimental flags@use-crux/indexer, @use-crux/local
runtimeRuntimeEngineDefinitionno durable Runtime EngineRuntime Engine APIs, wake handlers, CLI
persistenceCruxPersistenceConfigno global storeflows, plans, tasks, Runtime Bridge reads
generationCruxGenerationConfigautoEscape: true; securityWarnings in developmentprompt resolution and generation
devtoolsCruxDevtoolsConfigno runtime devtools transport unless installed elsewherelocal devtools plugin and Runtime Bridge
observabilityCruxObservabilityConfigno explicit export transportcanonical observability transport
pluginsreadonly CruxPlugin[][]runtime plugin installer

quality

Quality config controls discovery, persistence, redaction, and run defaults. Model, judge, and embedding providers belong in eval code, not in config.

OptionTypeDefaultNotes
quality.idstringnearest package.json nameWorkbench id stored on Quality records.
quality.dirstring.crux/qualityPersistence root for experiments, baselines, cassettes, and workbench state.
quality.includestring | readonly string[]['evals/**/*.eval.ts', '**/*.eval.ts']Discovery globs relative to the project root.
quality.excludestring | readonly string[]dependency/output defaultsExtra discovery excludes.
quality.redactreadonly string[]built-in secret redaction onlyDot paths removed from persisted records and cassettes.
quality.defaults.trialsnumber1Default executions per evaluation cell.
quality.defaults.concurrencynumber5Default concurrent cell execution.
quality.defaults.timeoutMsnumber60_000Default per-cell timeout.
quality.defaults.replayReplayModeliveDefault cassette behavior.
crux.config.ts
export default config({
  quality: {
    id: 'support-agent',
    dir: '.crux/quality',
    include: ['evals/**/*.eval.ts', 'src/**/*.eval.ts'],
    redact: ['customer.email', 'metadata.privateNote'],
    defaults: {
      trials: 2,
      concurrency: 4,
      timeoutMs: 90_000,
      replay: 'record-new',
    },
  },
})

See Quality and crux quality.

lint

Lint config controls authored Project Index findings. It does not run ESLint or TypeScript checks.

OptionTypeDefaultNotes
lint.profile'off' | 'recommended' | 'strict' | 'experimental'recommendedSelects which Project Index findings are exposed.
lint.rulesRecord<string, CruxLintRuleConfig>{}Rule overrides keyed by stable rule id.
lint.rules[id].enabledbooleanrule enabled by profileSet false to disable a rule project-wide.
lint.rules[id].severity'info' | 'warning' | 'error'rule defaultOverrides the displayed severity.
crux.config.ts
export default config({
  lint: {
    profile: 'strict',
    rules: {
      'definition.missing_eval_coverage': { severity: 'error' },
      'prompt.missing_output_schema': { enabled: false },
    },
  },
})

See Index Lint.

indexer

Indexer config is inert data stored by @use-crux/core and enforced by @use-crux/indexer. Loading an allowlisted extension imports JavaScript from node_modules; treat it like trusted build tooling.

OptionTypeDefaultNotes
indexer.extensionsreadonly CruxIndexerExtensionReference[][]Explicit extension package references.
indexer.extensions[].packagestringrequiredPackage specifier, for example @acme/crux-indexer.
indexer.extensions[].exportstringdefaultExport to read from the package.
indexer.extensions[].versionstringinstalled package acceptedExpected package version range.
indexer.extensions[].enabledbooleantrueSet false to keep a reference configured but unloaded.
indexer.extensions[].optionsunknownundefinedExtension-owned options.
indexer.trust.mode'first-party-only' | 'allowlisted' | 'unsafe-local-dev'first-party-onlyTrust posture before importing extension packages.
indexer.trust.allowreadonly string[][]Package/manifest names allowed in allowlisted mode.
indexer.trust.denyreadonly string[][]Deny list; deny entries win over allow entries.
indexer.rulesRecord<string, unknown>{}Extension-specific rule options keyed by rule id.
crux.config.ts
export default config({
  indexer: {
    extensions: [
      {
        package: '@acme/crux-indexer',
        export: 'default',
        version: '^1.0.0',
      },
    ],
    trust: {
      mode: 'allowlisted',
      allow: ['@acme/crux-indexer'],
    },
  },
})

See Writing an Indexer Extension.

experimental.indexer

Experimental config is unstable by design. Options here may move, rename, or graduate before a stable release. Stable-looking backend switches do not belong under indexer.

OptionTypeDefaultNotes
experimental.indexer.nativeboolean | CruxExperimentalIndexerNativeConfigfalseSelects the experimental native semantic backend.
experimental.indexer.native.engine'tsgo'tsgoTypeScript-Go native-preview semantic engine.
experimental.indexer.native.tsserverPathstringresolved from project dependenciesOptional TypeScript-Go executable path.
experimental.indexer.nativeAstboolean | CruxExperimentalIndexerNativeAstConfigfalseSelects the experimental native static AST/source frontend.
experimental.indexer.nativeAst.frontend'oxc'oxcRust/Oxc static frontend hosted by the local Go runtime.

experimental.indexer.native and experimental.indexer.nativeAst are independent:

  • native controls semantic enrichment. When enabled, TypeScript-Go owns semantic project setup, checker calls, declaration lookup, and AST traversal.
  • nativeAst controls the first static AST/source pass. It can use Rust/Oxc without selecting the native semantic backend.

experimental.indexer.nativeAst is covered by the native AST beta gate before release promotion: Rust/Oxc output must match the TypeScript Static Index baseline after canonical normalization, including lint findings, source graph rows, cache behavior, and TypeScript extension fallback. Node can still start for config inspection or trusted TypeScript extension work. If native AST diagnostics report worker startup, fallback, or Node-start failures, remove the flag or set it to false to return to the TypeScript baseline.

crux.config.ts
export default config({
  experimental: {
    indexer: {
      native: {
        engine: 'tsgo',
        tsserverPath: '/path/to/tsgo',
      },
      nativeAst: {
        frontend: 'oxc',
      },
    },
  },
})

See Project Index semantic backend selection.

generation

Generation config installs cross-cutting behavior for prompt resolution and adapter execution. Model choices usually belong in prompt, agent, eval, or adapter code.

OptionTypeDefaultNotes
generation.middlewarePromptMiddlewareundefinedGlobal generate/stream middleware. Plugins can layer additional middleware.
generation.tokenizerTokenizerFncharacter estimateCustom token counting for budgets and related helpers.
generation.autoEscapebooleantrueEscapes top-level string input fields before context gates and system/prompt functions receive them.
generation.securityWarningsbooleantrue when NODE_ENV !== 'production', else falseEmits warnings for suspicious input patterns.
crux.config.ts
export default config({
  generation: {
    tokenizer: (text) => Math.ceil(text.length / 4),
    autoEscape: true,
    securityWarnings: process.env.NODE_ENV !== 'production',
  },
})

See Prompt execution and Security warnings.

runtime

Runtime config installs the durable Runtime Engine used by name-bound, event-bound, time-bound, and background APIs.

crux.config.ts
import { config } from '@use-crux/core'
import { serverless } from '@use-crux/core/runtime'
import { postgres } from '@use-crux/postgres/runtime'
import { qstash } from '@use-crux/upstash/runtime'

export default config({
  runtime: serverless({
    store: postgres(),
    wake: qstash(),
  }),
})

Use node() for local/test execution, serverless({ store, wake }) for HTTP-woken deployments, or convex() from @use-crux/convex/runtime for Convex-hosted runtime work. Runtime config is a stable top-level domain, not experimental.runtime.

See Runtime Engine for the API reference and Runtime guides for deployment shapes.

persistence

Persistence config installs explicit runtime records. Crux does not infer durability, tenancy, or data locality from source discovery.

OptionTypeDefaultNotes
persistence.recordsRecordStoreundefinedRequired for durable flows, plans, tasks, and Runtime Bridge record reads.
crux.config.ts
import { inMemoryRecordStore } from '@use-crux/core/storage'

export default config({
  persistence: {
    records: inMemoryRecordStore(),
  },
})

See Storage.

devtools

Use devtools for the local UI/control/tunnel domain and Runtime Bridge peers. Use observability.serverUrl when application runtime code should send records to a known local server or tunnel.

OptionTypeDefaultNotes
devtools.serverUrlstringundefinedInstalls the local devtools transport plugin when observability does not own the transport.
devtools.bridgeboolean | RuntimeBridgeOptionsfalseEnables the local command plane.
devtools.bridge.enabledbooleantrue when object form is providedTurns an object-form bridge on or off.
devtools.bridge.urlstringderived from serverUrl and transport defaultsPreferred command transport URL.
devtools.bridge.connectUrlstringurlAlias for outbound WS connection behavior.
devtools.bridge.transport'ws' | 'http'runtime defaultws for long-lived Node peers; http for framework/serverless peers.
devtools.bridge.runtimeNamestringinferredHuman-readable peer name.
devtools.bridge.labelsRecord<string, string>{}Extra peer labels surfaced in local tooling.
devtools.bridge.heartbeatMsnumbertransport defaultPeer heartbeat interval.
devtools.bridge.reconnectboolean | { minMs?: number; maxMs?: number }transport defaultWS reconnect behavior.
crux.config.ts
export default config({
  devtools: {
    serverUrl: 'http://localhost:4400',
    bridge: {
      transport: 'ws',
      runtimeName: 'local-node-worker',
    },
  },
})

See Devtools Integration and Runtime Bridge.

observability

Use observability for deliberate export, tunnel/serverless delivery, or custom transport behavior. For ordinary same-machine local devtools, devtools.serverUrl or withDevtools() is still enough.

When observability.enabled is false, Crux explicitly disables the configured observability transport for this config instance. When observability.serverUrl is set, Crux creates an HTTP transport to that server. When observability.token is set, that token is sent as bearer auth for the scoped devtools ingest endpoint. When observability.transport is set, that custom transport wins.

OptionTypeDefaultNotes
observability.enabledbooleantrueSet false to disable an existing configured transport.
observability.serverUrlstringundefinedCreates an HTTP observability transport.
observability.tokenstringCRUX_DEVTOOLS_TOKEN when availableBearer token for tunneled devtools ingest.
observability.transportCruxObservabilityTransportundefinedCustom transport with send(records).
observability.delivery.maxPendingDeliveriesnumber1000Maximum in-flight transport sends before records remain queued.
observability.delivery.maxQueuedRecordsnumber2048Maximum retained queued plus in-flight records before oldest queued records are dropped and counted.
observability.delivery.retryDelayMsnumber100Base retry delay after a failed transport send.
observability.delivery.maxRetryDelayMsnumber5000Maximum capped retry delay after repeated transport failures.
crux.config.ts
export default config({
  observability: {
    serverUrl: process.env.CRUX_OBSERVABILITY_URL,
    token: process.env.CRUX_DEVTOOLS_TOKEN,
    delivery: {
      maxPendingDeliveries: 250,
      retryDelayMs: 100,
      maxRetryDelayMs: 5000,
    },
  },
})

See Observability.

plugins

Plugins install hooks in order. Each plugin receives the cumulative hook state from all previous plugins. Cleanup runs when the returned Crux object is disposed. Cleanup hooks may return a promise; Crux starts cleanup in reverse install order, and plugin runners that expose async disposal wait for those promises before reporting shutdown complete.

OptionTypeDefaultNotes
pluginsreadonly CruxPlugin[][]Ordered plugin list.
plugins[].namestringrequiredUnique name for debugging and diagnostics.
plugins[].install(hooks: Readonly<CruxHooks>) => CruxPluginResultrequiredReturns hook fields to merge plus optional cleanup.
crux.config.ts
export default config({
  plugins: [
    {
      name: 'my-plugin',
      install(hooks) {
        return {
          observability subscribers: {
            tool.call start records(event) {
              console.log(event.toolName)
            },
          },
        }
      },
    },
  ],
})

See Observability plugins.

Inspect Effective Config

Use crux config inspect to see the effective config, defaults, origins, and config-load diagnostics:

crux config inspect
crux config inspect --json
crux config inspect --cwd packages/backend
crux config inspect --config crux.config.ts

Every value is tagged with where it came from: default, config, package metadata, or a bound runtime value. Broken config imports degrade to an all-defaults view with diagnostics instead of failing the inspection command.

See crux config.

On this page