Import GNO directly into a Bun or TypeScript app with createGnoClient and the same local retrieval engine.
The SDK is for apps that want GNO in-process. Use it when a local Bun or TypeScript app needs search, retrieval, document access, indexing, or graph navigation without shelling out to gno or running gno serve.
bun add @gmickel/gnoimport { createDefaultConfig, createGnoClient } from "@gmickel/gno"
const config = createDefaultConfig()
config.collections = [{
name: "notes",
path: "/Users/me/notes",
pattern: "**/*",
include: [],
exclude: [],
}]
const client = await createGnoClient({
config,
dbPath: "/tmp/gno-sdk.sqlite",
})
await client.index({ noEmbed: true })
const results = await client.search("authentication")
for (const hit of results.results) {
console.log(hit.uri, hit.score)
}
await client.close()client.search(query, opts?) — BM25 keyword search for exact words, phrases, filenames, and identifiers.client.vsearch(query, opts?) — vector similarity for conceptual matches.client.query(query, opts?) — hybrid retrieval with expansion, default bounded graph candidates, fusion, and reranking controls. Pass { graph: false } to skip graph expansion or { explain: true } for a retrieval scoring receipt.client.ask(question, opts?) — retrieval-only or cited answer generation. Pass { verify: true } for the closed-Capsule claim-verification path and { explain: true } for the same retrieval scoring receipt.client.get(ref) and client.multiGet(refs) — fetch indexed documents by URI, doc ID, or path.client.getSections(ref) — heading outline for a document. Readable anchors stay compatible with the Web UI.client.createSectionTarget(ref, selector) and client.resolveSectionTarget(ref, target) — durable section locators with the same fail-closed citation semantics as REST and MCP. Prefer ordinary get/search for default retrieval.client.links(ref), client.backlinks(ref), client.similar(ref) — navigate direct and semantic relationships.client.list() and client.status() — inspect document inventory and index health.client.capture(input) — write a note with provenance and an optional presetId, then return the same capture receipt shape as CLI, API, and MCP. Content is text-only, collision policies are runtime-validated, legacy overwrite is not accepted, and writes fail on late-arriving files.client.previewRenameNote() / client.previewMoveNote(), followed by client.renameNote() / client.moveNote() — inspect a canonical impact plan, then apply only its exact version, digest, and confirmation. Supported reference edits and the source move share one all-or-rollback filesystem boundary.client.update(), client.embed(), client.index() — sync files, embed chunks, or do both.client.close() — release file handles and SQLite connections.Retrieval options may include up to 16 projectHints. SDK hints are opaque and untrusted, are never resolved or reflected, never trigger filesystem access, and currently have zero ranking effect. Leaving the option absent preserves existing results exactly.
const verified = await client.ask("Who owns the launch decision?", {
verify: true,
contextBudgetTokens: 12_000,
})
if (verified.verification?.claims.answerStatus === "abstained") {
console.log(verified.verification.claims.abstentionReason)
}Verified SDK results use the same contract as CLI, REST, MCP, and Web: the closed Capsule and freshness receipt, exact evidence IDs, lines, and hashes, four-state claim verdicts, 100% support threshold, and explicit semantic-verifier degradation. Raw client.ask() behavior remains compatible when verify is not true.
Indexed URIs such as gno://notes/plan.md?index=research select and read that named database, even when the client was created for another index. Missing indexes fail without being created. Keep each multiGet() batch on one index.
When private retrieval tracing is enabled, search, query, ask, get, and Context Capsule results carry a non-enumerable RETRIEVAL_TRACE_METADATA symbol. It exposes the local receipt ID without changing JSON output. Read it with getRetrievalTraceMetadata(result), then pass traceId to client.get() to record the exact opened line range against an open query receipt.
const receipt = await client.capture({
collection: "notes",
title: "Customer call",
content: "Follow up on renewal timeline.",
presetId: "meeting",
tags: ["customer", "follow-up"],
source: {
kind: "meeting",
title: "Customer call",
},
})
console.log(receipt.uri, receipt.contentHash)const plan = await client.previewRenameNote({
ref: "gno://notes/old-note.md",
name: "new-note.md",
})
if (plan.canApply) {
const result = await client.renameNote({
ref: "gno://notes/old-note.md",
name: "new-note.md",
schemaVersion: plan.schemaVersion,
planDigest: plan.planDigest,
confirmation: "apply",
})
console.log(result.status)
}Define collections in code when your app owns the workspace, or point at an existing GNO config when the user already has one.
const fromExisting = await createGnoClient({
configPath: "/Users/me/Library/Application Support/gno/config/index.yml",
})
const noDownloads = await createGnoClient({
config,
downloadPolicy: { offline: false, allowDownload: false },
})Always call await client.close() when your process is shutting down. After close, further calls throw a GNO SDK error. This matters for desktop apps, tests, and short-lived automations because the SDK holds SQLite connections and file handles.