SDK

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.

What this is

Install

bun add @gmickel/gno

Basic usage

import { 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 APIs

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)
}

Configuration patterns

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 },
})

Lifecycle

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.