Knowledge Bases
Index, reindex, remove, retrieve, ground, scope, and inspect a Connected Knowledge base.
knowledgeBase() is the high-level facade for indexing and reading one corpus.
Use it when one definition should own storage, embeddings, lifecycle, retrieval,
tools, grounding, views, assertions, communities, and namespace scoping.
Use the lower-level Retrieval APIs when you only need a custom retriever() or
direct indexer() control. See Querying and
Indexing documents for those layers.
Minimal Setup
import { embedding } from "@use-crux/core/embedding";
import { knowledgeBase } from "@use-crux/core/knowledge";
import { inMemoryStorage } from "@use-crux/core/storage";
const dense = embedding({
kind: "dense",
name: "demo-dense",
dimensions: 2,
maxInputTokens: 1000,
batch: { maxSize: 8 },
embed: async (inputs) =>
inputs.map((input) => {
const text = input.type === "text" ? input.text.toLowerCase() : "";
return text.includes("refund") ? [1, 0] : [0, 1];
}),
});
const docs = knowledgeBase({
id: "docs",
storage: inMemoryStorage(),
embeddings: dense,
});
await docs.index([
{
namespace: "docs",
sourceId: "refunds.md",
content: "Refunds are available within 30 days of purchase.",
metadata: { section: "policy" },
},
]);
const hits = await docs.retriever({ limit: 3 }).retrieve("refund window");id is the stable identity used by the indexer, retriever, recipe traces, and
knowledge records. If you do not pass a corpus, the default namespace is the
same as id.
Configuration
| Option | Use it for |
|---|---|
storage | One bundle that supplies records and search. |
records and search | Explicit store overrides when you do not use a bundle. |
embeddings | Dense vector indexing and dense retrieval. |
sparseEmbeddings | Sparse or fused retrieval. |
chunking | Shorthand chunking options for simple document indexing. |
pipeline | Named indexing pipeline with transforms, chunker, cache identity, and derive stages. |
metadataSchema | Runtime metadata validation plus typed filters and views. |
lifecycle.retention | cleanup by default, or retain-inactive for replaced generations. |
source | Deferred input used when index() or reindex() is called without arguments. |
corpus | Reuse an existing corpus ledger. Its id must match the knowledge base id. |
communities | Community report configuration for broad connected search. |
Pass either chunking or pipeline, not both:
import { chunker, indexingPipeline } from "@use-crux/core/indexing";
const docs = knowledgeBase({
id: "docs",
storage,
embeddings: dense,
pipeline: indexingPipeline({
chunker: chunker.structured({ maxChars: 1200, overlapChars: 150 }),
}),
});Use Embeddings and Chunkers for the embedding and chunking choices. The Connected Knowledge pages focus on the surfaces built on top.
Index, Reindex, And Remove
index() replaces each source it receives. reindex() treats the input as a
complete source set and removes stale sources. remove(sourceId) deletes a
source from active retrieval immediately.
await docs.index([
{ namespace: "docs", sourceId: "a.md", content: "Alpha" },
]);
await docs.reindex([
{ namespace: "docs", sourceId: "b.md", content: "Beta" },
]);
await docs.remove("b.md");When metadataSchema is configured, invalid metadata is rejected per source.
Direct indexing can write valid sources and then throw an aggregate metadata
error for invalid sources. Corpus-backed indexing reports invalid sources as
failed source outcomes.
Mutation Effects
Public source mutations emit native Effect receipts for the durable mutation:
index(), reindex(), remove(sourceId), and corpus-backed sync. The receipt
uses the knowledge-base id, namespace, operation, source ids, and safe outcome
counts as its resource summary. Inspect and recover these receipts through the
Effects APIs; knowledge bases do not keep a separate
receipt store.
Recovery is audit-first. index() reports recovery unavailable: an interrupted
generation replacement normally leaves the prior active records serving until a
new generation durably publishes, but Crux does not keep a generic restore
handler for the mutation. reindex() and remove() report irreversible because
they can delete active sources or stale sources that cannot be reconstructed
from the receipt. Corpus-backed index() reports unavailable, while
corpus-backed reindex() reports irreversible when it deletes stale sources.
Connected Knowledge derivation, claim compilation, view maintenance, retrieval, and community materialization are ordinary internal work. They run after the mutation receipt settles, and their failures do not change the mutation Effect outcome.
Read Surfaces
A knowledge base exposes the Retrieval surfaces directly:
const retriever = docs.retriever({ search: { dense: true }, limit: 6 });
const recipe = docs.recipe();
const tools = docs.tools({ prefix: "docs", include: ["search", "getSource"] });
const grounded = docs.grounding({
query: ({ input }) => input.question as string,
citations: { required: true, quotes: "required" },
});recipe() with no arguments creates a default retrieve() recipe. Pass steps
when retrieval should include relation expansion, reranking, compression, or
global search.
Scope By Namespace
Use scope() when one definition serves multiple tenants or logical corpora.
The scoped handle changes storage keys and graph visibility, not just a query
filter.
const tenantDocs = docs.scope({ namespace: "tenant-a" });
await tenantDocs.index([
{
namespace: "tenant-a",
sourceId: "policy.md",
content: "Tenant A policy.",
},
]);
const tenantHits = await tenantDocs.retriever().retrieve("policy");Scoped handles do not expose scope() again. Views, assertions, communities,
relations, and hydrated recipe hits stay inside the scoped namespace.
Inspect Lifecycle
const inspection = docs.inspect();
console.log(inspection.namespace);
console.log(inspection.capabilities.legs.lexical);
console.log(inspection.lifecycle.indexedSources);inspect() reports configured storage, per-leg search capabilities, fusion, filter
support, source strategy, and lifecycle counters. It does not read retrieved
content.