Configuration

September 6, 2026 · View on GitHub

GNO configuration reference.

Config File

Location varies by platform (see File Locations below). Run gno doctor to see your resolved config path.

version: "1.0"

# FTS tokenizer (set at init, cannot change)
ftsTokenizer: snowball english

# SQLite busy_timeout in ms (default 60000; range 1000-600000).
# Raise for long embedding passes on slow disks.
# busyTimeoutMs: 60000

# Trusted local CLI project affinity
projectAffinity:
  enabled: true
  contribution: 0.03

# Collections
collections:
  - name: notes
    path: /Users/you/notes
    pattern: "**/*.md"
    egressPolicy: local_only
    # Source content availability (distinct from egressPolicy):
    # any (default) = legacy reads; local = opt-in no-materialization guard
    # for tested macOS File Provider layouts only.
    sourceAvailability: any
    # Memory substrate flag: only collections with memoryManaged: true accept
    # gno remember writes (omitted = false).
    # memoryManaged: true
    include: []
    exclude:
      - .git
      - node_modules
    languageHint: en

  - name: work
    path: /Users/you/work/docs
    pattern: "**/*"
    exclude:
      - dist
      - build

# Contexts (semantic hints)
contexts:
  - scopeType: global
    scopeKey: /
    text: Personal knowledge base and project documentation

  - scopeType: collection
    scopeKey: notes:
    text: Personal notes and journal entries

  - scopeType: prefix
    scopeKey: gno://work/api
    text: API documentation and specifications

# Model configuration
models:
  activePreset: slim-tuned

# Optional schema-lite content type rules
contentTypes:
  - id: person
    prefixes:
      - people/
      - contacts/
    preset: person
    graphHints:
      - mentions
      - works_at
    searchBoost: 1.15
  - id: meeting
    prefixes:
      - meetings/
    preset: meeting
    temporal: true

# Optional terminal hyperlink target template for CLI search output
editorUriTemplate: "vscode://file/{path}:{line}:{col}"

# Optional resident Streamable HTTP MCP gateway
gateway:
  host: 127.0.0.1
  enableWrite: false
  toolProfile: full

# Private local retrieval receipts are absent/off by default.
retrievalTraces:
  enabled: false

# Daemon-only scheduled findings pass (report-only audit -> records). Off by default.
# findings:
#   enabled: true
#   cadence: 6h
#   collection: findings

Project-profile apply also maintains optional projectProfileBindings entries in this local config. Each timestamp-free entry binds a canonical absolute .gno/index.yml path to its SHA-256 fingerprint and projected collection. These machine-local provenance records are written under the shared config lock; they are never copied into the tracked profile or exposed by public profile receipts.

Project affinity

projectAffinity.enabled defaults to true and controls only the fallback cwd-derived signal from user config. Set it to false to disable that fallback. Explicit --project-root values and a valid nearest project profile retain their higher-precedence request-local behavior. Use --no-project-affinity to disable every affinity source for one request. projectAffinity.contribution defaults to 0.03 and must stay within 0..0.03.

Only canonicalized local CLI roots can match configured collection paths. Explicit --project-root values replace profile/cwd inference. When no explicit root is supplied, the nearest valid .gno/index.yml contributes its compiled, request-local affinityDefaults; otherwise the user config applies to the cwd-derived root. Profile defaults never overwrite this user default, and source metadata/content types never become project identity. Overlapping/duplicate roots never stack. SDK, REST, MCP, and Web UI projectHints are opaque/untrusted (maximum 16) and deliberately produce zero affinity without filesystem probing. All auxiliary contributions share the ±0.08 cap; collection/tag/date/exclude/egress filters remain hard.

See Project-Local Retrieval Profiles.

File and export adapters

Collections automatically recognize .jsonl, .ndjson, .eml, .mbox, .ics, .vtt, .srt, and explicit .browser-export files. Optional recordAdapters configuration provides closed JSONL field mappings and explicit JSON/text transcript selection; it cannot execute code. See File and Export Adapters for the support matrix, fixed resource and 60-second adapter deadline caps, partial-import receipts, snapshot/tombstone semantics, and no-live-account security boundary. When include is empty, configuring recordAdapters.transcript.format: json also makes .json files discoverable. A nonempty include remains an explicit allowlist and must list .json.

Collection egress policy

Every collection has one effective boundary: local_only, lan, or remote. An omitted value fails closed to local_only. Collections created before the policy migration retain their indexed documents and lexical/vector data, but their cached provenance is legacy_default; newly synchronized collections without an explicit value report config_default. Neither default can imply lan or remote.

PolicyPermitted destination
local_onlyLocal process, local files, loopback clients, and loopback model servers only
lanlocal_only plus authenticated, proven private-network peers
remotelan plus authenticated public transport and pinned HTTPS model providers

Authentication and write permission remain separate gates. A token does not relax collection policy; gateway.enableWrite does not relax it either. Mixed evidence and derived artifacts use the most restrictive participating collection. Explicit partial checks disclose every omitted collection and reason; normal operations never silently drop restricted evidence.

Memory-managed collections

collections[].memoryManaged is optional (true | omitted). It declares the collection as a GNO-managed memory substrate: gno remember writes one fact file per record there (facts/<date>/mem-<id>.md with memory: frontmatter carrying record id, scopes, caller/session identity, timestamp, content hash, and relations.supersedes edges) and refuses to write into any collection without the flag. gno recall reads only from memory-managed collections. Ordinary indexing, search, and egress policy are unaffected by the flag; malformed hand-edited records are reported by gno status and gno audit.

collections:
  - name: memory
    path: /Users/you/notes/memory
    pattern: "**/*.md"
    memoryManaged: true

Source availability

collections[].sourceAvailability is optional and independent of egressPolicy. Exact values: any | local. Omitted means any.

ModeBehavior
anyDefault. Legacy source reads; no no-materialization guard.
localOpt-in. Indexes only content that is already local; refuses cloud-placeholder materialization on the macOS File Provider layouts covered by physical evidence. Unsupported setup fails closed.

What local does (macOS File Provider, evidence-qualified):

  • Establishes a process-scoped no-materialization I/O policy (IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES) for content reads.
  • Classifies directories hierarchically (memoized per operation) before descent; does not add one availability syscall per discovered file.
  • Rechecks at the content boundary (sniff, hash, conversion, record import, targeted sync, and watch-triggered ingestion share the same guard).
  • Skips cloud placeholders / partial content as CLOUD_PLACEHOLDER / CLOUD_PARTIAL (not conversion errors).
  • Refuses descent into dataless or availability-unknown directories (DATALESS_DIRECTORY or fail-closed codes) and preserves previously indexed descendants under those unproven prefixes rather than proving deletion.

Evidence scope (do not over-claim):

  • Proven independently for Google Drive, iCloud Drive, and OneDrive on the tested macOS/provider configuration.
  • OneDrive is claimed only for both installed immediate SharePoint library roots under the SharedLibraries domain — not the aggregation root, not arbitrary deeper trees, not untested library layouts.
  • No Windows Cloud Files or Linux/FUSE guarantee.
  • Metadata or provider bookkeeping may still occur. GNO local mode does not pin, evict, or download as product behavior.
  • Availability controls source materialization; egress controls where derived data may travel.

Measured scan cost: on the controlled 5,000-file all-local Markdown corpus (2 warmups, 9 retained interleaved samples per lane), production traversal measured 215.1020 ms for pre-implementation any, 212.6756 ms for current any (-1.1280%, within the 3% budget), and 215.1938 ms for hierarchical local (+1.1841% versus current any, within the 10% budget). Conversion and embedding were not applicable to this corpus; raw receipts are tracked under research/file-provider/evidence/.

collections:
  - name: drive-notes
    path: /Users/you/Library/CloudStorage/GoogleDrive-…/My Drive/notes
    pattern: "**/*"
    sourceAvailability: local
    egressPolicy: local_only

Inspect and change one policy with:

gno collection policy get notes
gno collection policy check --action remote_inference \
  --destination remote --content-class source -c notes \
  --authenticated --authorized --explain-egress
gno collection policy set notes remote --confirm-relaxation 0
gno collection policy set notes local_only

The relaxation revision must exactly match the current get result. It is single-use and becomes stale after any intervening policy change. Tightening needs no confirmation and invalidates resident sessions, active streams, queued jobs, and saved authorization state; callers must retry against the new policy. Removing an explicit policy does not restore network access—it returns to the fail-closed local default.

Migration does not recall data already disclosed. Tightening a collection blocks future GNO-controlled transfers, but an artifact previously uploaded to a remote service may require deletion or takedown at that service. For gno.sh, unpublish or delete the hosted item in Studio, including public publications. Unpublish denies access but retains source and history; deletion denies access before durable cleanup. Neither removes local files or independent copies. Encrypted artifacts remain client-encrypted; gno.sh never receives the passphrase and cannot decrypt or recover them.

Policy decisions create bounded, content-free local audit receipts. Use gno egress-audit list|show|status|delete|purge; receipts contain stable reason codes and redacted collection identity, never query text, document content, credentials, target URLs, or sensitive absolute paths.

Knowledge-integrity audit policy

Knowledge-integrity audits are run-scoped: there is no persisted baseline, suppression list, or automatic repair. The only schedule is the daemon's opt-in scheduled findings pass below, which runs the same read-only audit and writes findings as records. Supply an age review threshold only when wanted:

gno audit freshness --max-age-days 90
gno audit links --orphan-root gno://notes/index.md \
  --orphan-ignore-prefix templates --orphan-ignore-prefix archive

The equivalent MCP fields are maxAgeDays, orphanRoots, and orphanIgnorePrefixes. Age is a review signal, not a factual-truth judgment. gno audit reads the effective collection definitions from this config but does not change them. Do not confuse its content-bearing local findings with the content-free egress policy receipts above.

Scheduled findings pass

gno daemon can run the read-only audit on a cadence and write each finding as a Markdown record into a collection you already configured. Off by default; gno serve and the CLI ignore the block.

findings:
  enabled: true # default false
  cadence: 6h # <n>s|m|h|d between 10s and 30d (default 6h)
  collection: findings # name of an existing entry in `collections`
FieldRequired when enabledNotes
enabled-false by default. The daemon never audits on cadence unless this is true.
cadencenoRun interval, validated at config parse. Cadence is a floor; an overrunning pass defers a tick.
collectionyesMust name an existing collection. The daemon never creates it or writes anywhere else.

Startup fails closed with a clear message when enabled is true and collection is unset or unknown, or when cadence is out of range. Records are finding-<id>.md files with type: finding frontmatter and finding / audit / category / severity tags; identity is the audit finding id (check + target + evidence), so repeat runs upsert, vanished findings resolve, and resolved records expire after 30 days (2000-record ceiling). Last-run state is persisted next to the index database and surfaced by gno daemon --status and gno doctor. Saved Context Capsule reverification is not part of this schedule. Full contract: Daemon Mode.

Resident HTTP MCP Gateway

gno serve and gno daemon expose /mcp. The default configuration binds literal 127.0.0.1, derives exact 127.0.0.1:<port> and localhost:<port> Host/Origin allowlists, and leaves mutation tools disabled.

gateway:
  host: 0.0.0.0
  tokenFile: ~/.config/gno/mcp-token
  allowedHosts:
    - workstation.local:3000
  allowedOrigins:
    - https://trusted-client.example
  enableWrite: false
  toolProfile: full
  limits:
    maxBodyBytes: 1048576
    maxRequestsPerMinute: 120
    maxConcurrentRequests: 64
    maxQueuedRequests: 16
    maxSessions: 32
    sessionIdleTimeoutMs: 300000

Wildcard or non-loopback binding requires tokenFile, allowedHosts, and allowedOrigins; every allowlist value is exact and wildcards are rejected. gno serve remains loopback-only because its Web and REST surfaces share the listener; use gno daemon for authenticated non-loopback MCP access. An explicitly configured missing token file is generated with a random 256-bit token and mode 0600 on POSIX. Authentication and mutation authorization are separate: enableWrite must be true before HTTP write tools are registered or dispatched. CLI gateway flags override config values for one invocation. toolProfile (core | full, default full) picks the advertised MCP tool set for every gateway client; --mcp-tool-profile overrides it, and the value is read once at listener start, so changing it means restarting gno serve or gno daemon. See Tool Profiles.

Upgrading from a stdio-only setup requires no client-config migration: gno mcp remains supported. Start gno serve or gno daemon only for clients that can use the resident URL http://127.0.0.1:3000/mcp. Stop any resident owner for the same data directory before switching between serve and daemon.

Browser clipper boundary

The browser clipper is not enabled by a gateway write flag. It exists only on loopback gno serve, uses a dedicated visibly approved, origin-bound capture grant, and is structurally absent from non-loopback listeners. MCP bearer tokens, GNO_API_TOKEN, and gateway.enableWrite never authorize clipping.

The extension targets literal http://127.0.0.1:<port>; its unpacked manifest grants only activeTab, scripting, storage, and http://127.0.0.1/*. There is no configuration for remote clipper access, cookie/history access, background surveillance, or source-URL fetching. See Browser Clipper for install, pairing, storage, and recovery details.

Private Retrieval Traces

Retrieval trace recording is local, opt-in, and disabled when retrievalTraces is absent or enabled: false. Enabling it requires an explicit redaction mode and every retention bound:

retrievalTraces:
  enabled: true
  redactionMode: metadata
  retention:
    maxAgeDays: 30
    maxTraces: 1000
    maxRecordsPerTrace: 10000
    maxBytes: 16777216

metadata stores content-free query/goal/filter shapes plus validated evidence identity such as source hashes, docids, ranks, and exact line spans. It does not store raw query, goal, filter values, passages, filesystem paths, or external URLs, and it is not replay-capable.

replay is separate, explicit consent to retain the normalized raw query, goal, and validated retrieval filters. Even in replay mode, event/run payloads use closed evidence schemas: no source passages, absolute paths, or external URLs are accepted. Receipts stay in the active local index database; the recorder has no telemetry or upload path.

Each traced application request creates one local session at the CLI, REST, MCP, or SDK boundary. It records normalized retrieval stages, exact canonical source spans, explicit open/cite/pin outcomes, capability fallbacks, and a terminal outcome. Search-result planner details remain internal and do not change public result JSON. Ask records only citations retained after final citation validation; failures, partial answers, and cancellations are terminal states, never implicit relevance judgments.

Random trace identity is response metadata only: CLI stderr, the X-GNO-Trace-ID REST header, MCP _meta.gno.retrievalTrace.traceId, or the SDK's non-enumerable RETRIEVAL_TRACE_METADATA symbol. It never enters a canonical Context Capsule or changes capsuleId. Retrieval-only CLI calls may be continued with gno get --trace-id <id>.

Retention uses epoch-millisecond timestamps and deterministically removes expired traces first, then traces exceeding the per-trace record limit, then the oldest traces until count and logical-storage byte bounds are satisfied. Changing maxAgeDays applies the shorter current policy to existing receipts.

Per-trace deletion removes every owned run, event, judgment, and export link transactionally, but SQLite WAL history may remain until checkpointed. Full purge enables SQLite secure deletion for the transaction and requires a successful truncating WAL checkpoint before reporting physical cleanup complete. User-created exports and external backups remain user-owned and must be deleted separately.

Disabling retrievalTraces.enabled stops new capture and fingerprint work; it does not make existing local receipts unmanageable. gno trace, the SDK, and the loopback REST/Web surfaces can still inspect, explicitly label, export, or delete stored receipts. Metadata-mode label references use an index-local, random redaction secret persisted in database metadata so retries stay stable across restarts without exposing the secret through any surface.

Trace schema upgrades and recovery

Trace storage is database schema v14. Existing v12 and v13 indexes upgrade transactionally: a failed migration preserves the prior schema version and does not leave partial trace tables. Stop the active resident owner and back up the SQLite database together with live -wal/-shm companions before moving an important index between GNO versions; never copy a database while it is being written.

There is no in-place downgrade command. Disabling recording is reversible and does not delete receipts. Use explicit export first when evidence must be retained, gno trace delete for one receipt, or gno --yes trace purge --json for all local trace rows. Only a purge receipt with physicalCleanup: completed proves WAL truncation; exported artifacts and external backups remain outside the purge boundary.

Verified Folder Setup State

gno setup <folder> is the preferred first activation path. It may create the default config, but the transaction itself stays direct and standalone: it does not discover or attach to a resident listener. Its fixed lexical stages are preflight, config_saved, store_synced, lexical_indexed, lexical_proved, and completed.

Private state lives outside the indexed folder:

<dataDir>/setup-receipts/<index>/<folder-fingerprint>.json
<dataDir>/setup-semantic/<index>/<folder-fingerprint>.json
<dataDir>/setup-semantic/<index>/<folder-fingerprint>.log

The closed FolderSetupReceipt@1.0 contains no semantic-worker or connector fields. setup-command-result@1.0 composes it with setup-semantic@1.0; setup-activation-result@1.0 wraps that unchanged result only when explicit connectors are selected. Stable semantic source identity ignores timestamps, stage tokens, and created/reused disposition, but changes with material folder, index, or activation evidence. --no-semantic starts no new work; a live one-shot worker keeps ownership of its canonical receipt and PID.

Connector setup never overwrites an existing entry. Malformed configuration is preserved for repair. Connector failures or unverifiable skill runtimes report completed_with_actions after lexical proof without rolling back the collection.

Collections

Collections define what gets indexed.

Collection Fields

FieldTypeDefaultDescription
namestringrequiredUnique identifier (lowercase)
pathstringrequiredAbsolute path to directory
patternglob**/*File matching pattern
includearraysee belowExtension allowlist
excludearraysee belowPatterns to skip
updateCmdstring-Shell command before indexing
languageHintstring-BCP-47 language code
modelsobject-Per-collection model overrides

Default Include Extensions

When include is empty (default), only supported document types are indexed:

  • .md - Markdown
  • .txt - Plain text
  • .pdf - PDF documents
  • .docx - Word documents
  • .pptx - PowerPoint
  • .xlsx - Excel spreadsheets

To override the default and index only specific supported types:

include:
  - .md
  - .txt

Note: include controls which files are scanned, but files must still have converter support. Specifying unsupported extensions will result in conversion errors.

Files larger than the conversion size limit (100MB default) are skipped via filesystem stat before GNO reads file bytes.

Files without extensions (e.g., Makefile, LICENSE) and dotfiles (e.g., .env, .gitignore) are always excluded.

Default Excludes

exclude:
  - .git
  - node_modules
  - .venv
  - .idea
  - dist
  - build
  - __pycache__
  - .DS_Store
  - Thumbs.db

Examples

Markdown notes:

- name: notes
  path: /Users/you/notes
  pattern: "**/*.md"

Code docs with language hint:

- name: german-docs
  path: /Users/you/docs/german
  pattern: "**/*.md"
  languageHint: de

Mixed documentation folder:

- name: project-docs
  path: /Users/you/project/docs
  pattern: "**/*"
  include:
    - .md
    - .txt
  exclude:
    - node_modules
    - dist
    - drafts

Note: Exclude patterns match path components (directory or file names), not globs. Use dist to exclude a dist/ directory, not *.js.

With update command:

- name: wiki
  path: /Users/you/wiki
  updateCmd: "git pull"

Contexts

Contexts add semantic hints to improve search relevance.

Scope Types

TypeKey FormatExample
global/Applies to all documents
collectionname:Applies to collection
prefixgno://collection/pathApplies to path prefix

Examples

contexts:
  # Global context
  - scopeType: global
    scopeKey: /
    text: Technical knowledge base for software development

  # Collection context
  - scopeType: collection
    scopeKey: notes:
    text: Personal notes and daily journal entries

  # Path prefix context
  - scopeType: prefix
    scopeKey: gno://work/api
    text: REST API documentation and OpenAPI specs

Contexts are operational retrieval guidance, not labels stored and forgotten. Structured CLI, REST, MCP, and SDK search results include an optional context field whenever a scope matches. GNO composes matching text deterministically: global first, collection second, then path prefixes from broadest to most specific. Duplicate text is included once, and prefix matching respects path segments (projects/a does not match projects/ab).

Context does not affect matching or ranking. Grounded Ask uses it as trusted user configuration, delimited separately from retrieved document content. A result with no matching context keeps the historical shape and omits the field.

Models

Model configuration for embeddings and AI answers.

Presets

PresetBest For
slim-tunedCurrent default; tuned query expansion
slimUntuned slim query expansion
balancedQwen2.5 3B expansion and answers
qualityQwen3 4B expansion and standalone AI answers

Actual download and cache use depends on the selected artifacts, quantization, and files already present. Treat UI size labels as orientation, not measured clean-install footprints.

Note: When using GNO standalone with --answer, the quality preset is required for documents containing Markdown tables or other structured content. The smaller models in slim/balanced presets cannot reliably parse tabular data. When GNO is used via MCP, skill, or CLI by AI agents (Claude Code, Codex, etc.), the agent handles answer generation, so any preset works for retrieval.

Per-collection model overrides

Collections can override model roles without replacing the global preset system.

Guides:

Example:

collections:
  - name: work
    path: /Users/you/work/docs
    models:
      rerank: "file:/models/work-rerank.gguf"
      expand: "file:/models/work-expand.gguf"

Resolution order:

  1. collection role override
  2. active preset role
  3. built-in default fallback

Notes:

  • overrides are partial; you only set the roles you need
  • global preset remains the base layer for everything else
  • collection-scoped overrides are only meaningful when an operation resolves a specific collection
  • the Web UI Collections page can now edit these overrides directly and shows effective per-role model resolution
  • use collection overrides when one collection should intentionally diverge from the workspace default
  • if a future benchmark shows a different code-specific embedding model wins on source-code retrieval, prefer using models.embed on code collections instead of replacing the global default for every collection

This still uses normal GNO model provisioning rules:

  • it auto-downloads on first use by default
  • it respects GNO_NO_AUTO_DOWNLOAD / offline policy the same way preset models do
  • it is most useful when one collection should diverge from the global default or when migrating older configs explicitly

Current general multilingual benchmark signal

The immutable April 2026 FastAPI-docs run used 15 documents in five corpus languages (en, de, fr, es, zh) and 13 queries:

A separate July 2026 Nemotron screen measured Qwen at 0.9891 vector / 0.9891 hybrid nDCG@10 and Nemotron at 0.9023 / 0.9461 on the same 13-query lane after runtime/profile changes. Nemotron used a temporary PyTorch HTTP adapter, so timings are not comparable; the screen did not validate an official production GGUF for Nemotron.

Qwen3-Embedding-0.6B-GGUF is the embedding model in all four built-in presets.

Operational consequences:

  • existing users who upgrade may need a fresh gno embed pass because their old vectors were created with bge-m3
  • GNO now counts readiness/backlog against the active embed model, so the need to re-embed is visible immediately after a preset/default change
  • if a future release changes the formatting profile for an active embedding model, re-embed is also required because the stored document vectors were produced differently

Scope matters: query-language classification is distinct from indexed-document language detection (en, de, fr, it, zh, ja, ko), and this small semantic fixture covers only five languages.

Model-free lexical fallback has a separate immutable July 22, 2026 CJK benchmark. Production BM25 lexical results and frozen floors:

  • Chinese: baseline Recall@10 0.2222, nDCG@10 0.1481, zero-result 0.7778; promotion Recall@10 0.4722, nDCG@10 0.3981, maximum zero-result 0.5278
  • Japanese: baseline Recall@10 0.125, nDCG@10 0.125, zero-result 0.875; promotion Recall@10 0.375, nDCG@10 0.375, maximum zero-result 0.625
  • Korean: baseline Recall@10 0.5, nDCG@10 0.5, zero-result 0.5; promotion Recall@10 0.75, nDCG@10 0.75, maximum zero-result 0.25

The promotion-gates.md also binds MRR, non-regression, and cost requirements. The Chinese fixture includes a genuine rank-7 retrieval failure. These lexical results do not describe semantic retrieval or select an implementation, and no production analyzer/configuration changed. All positive qrels use relevance 3, so nDCG measures placement but not distinctions among positive gain grades.

The legacy multilingual Evalite lane is a four-case BM25-only sanity check, not a release gate.

Model Details

All presets use:

  • Qwen3-Embedding-0.6B for embeddings (multilingual)
  • Qwen3-Reranker-0.6B for reranking (scores best chunk per document)
PresetEmbedRerankExpandGen
slim-tunedQwen3-Embedding-0.6B-Q8Qwen3-Reranker-0.6B-Q8GNO slim retrieval tuneQwen3-1.7B-Q4
slimQwen3-Embedding-0.6B-Q8Qwen3-Reranker-0.6B-Q8Qwen3-1.7B-Q4Qwen3-1.7B-Q4
balancedQwen3-Embedding-0.6B-Q8Qwen3-Reranker-0.6B-Q8Qwen2.5-3B-Q4Qwen2.5-3B-Q4
qualityQwen3-Embedding-0.6B-Q8Qwen3-Reranker-0.6B-Q8Qwen3-4B-Q4Qwen3-4B-Q4

Reranking scores the best retrieved chunk per document, capped at 4K characters. The model's larger advertised context window does not mean GNO sends complete documents to the reranker.

CLI retrieval commands (gno search, gno vsearch, gno query) can emit OSC 8 hyperlinks in terminal output when stdout is a TTY.

Configure the target URI template in YAML:

editorUriTemplate: "vscode://file/{path}:{line}:{col}"

Or override it via environment:

export GNO_EDITOR_URI_TEMPLATE="vscode://file/{path}:{line}:{col}"

Precedence:

  1. GNO_EDITOR_URI_TEMPLATE
  2. editorUriTemplate in index.yml
  3. default fallback file://{path}

Supported placeholders:

  • {path} absolute filesystem path
  • {line} best-effort line number from the result snippet, when available. Default snippets skip leading YAML frontmatter, so this line follows the trimmed display range rather than the raw source fence.
  • {col} best-effort column placeholder (1 when line is available)

If the chosen template requires {line} but a result has no line hint, GNO falls back to plain text for that result instead of inventing :1.

Content Types

contentTypes is an opt-in, schema-lite typing layer for second-brain pages. It is not a mutable ontology. Empty or absent contentTypes keeps legacy behavior.

contentTypes:
  - id: person
    prefixes: [people/, contacts/]
    preset: person
  - id: meeting
    prefixes: [meetings/]
    preset: meeting
    temporal: true

Fields:

FieldTypeDescription
idstringStable content type ID, such as person or meeting
prefixesstring[]Relative path prefixes that map documents to this type
presetstringNote preset used by future type-aware creation and ingestion behavior
temporalbooleanAccepted metadata flag for time-oriented pages
searchBoostnumberBounded soft ranking factor from 0.5 to 2; default/neutral is 1
graphHintsstring[]Ordered typed-edge hints for link projection, traversal, and diagnose

Validation is warning-based after YAML parsing:

  • unknown preset references warn and the content type entry is dropped
  • exact duplicate prefixes are deduped
  • overlapping prefixes are retained, for example people/ and people/team/
  • rules are normalized longest-prefix-first for matching and edge derivation

searchBoost applies only after a document is already a valid retrieval candidate. One canonical configured type wins: a configured frontmatter type ID takes precedence, otherwise longest-prefix matching applies. Boosts never stack and arbitrary categories cannot trigger them. Factors map linearly onto a contribution from -0.05 through +0.05; project affinity and all other auxiliary signals share the final ±0.08 cap. BM25 and vector apply the contribution after score normalization and before their existing cutoff/order; hybrid applies it to normalized fusion before rerank blending, so rerank and lexical top-hit protection remain the final ordering authority. Content-type rules do not widen retrieval or disable minScore. Scores stay clamped to 0..1; collection, tag, date, category, author, and exclude filters remain hard.

Use gno query --explain or gno ask --explain to inspect raw/base score, configured factor, requested/capped contribution, combined auxiliary cap, final score, rule source, and ranking fingerprint. gno query diagnose exposes the same component as schema v1.2 when active. gno status --json, REST status, MCP gno_status, and SDK client.status() show the effective rule IDs/factors and fingerprint without exposing configured path prefixes. A boost-only config edit takes effect immediately and does not reconvert documents or rebuild vectors.

graphHints vocabulary is centralized in the config API. Supported hints are mentions, works_at, attended, decided, and related_to. Hints do not create standalone edges because they have no target. Instead, the first hint types projected wiki/markdown links for matching documents, and remaining hints surface in gno graph query and gno query diagnose metadata. Editing graphHints changes the content-type fingerprint, so unchanged documents are reprocessed on the next sync and typed edges are re-derived.

Custom Models

Need a more example-driven guide?

models:
  activePreset: custom
  presets:
    - id: custom
      name: My Custom Setup
      embed: hf:user/model/embed.gguf
      rerank: hf:user/model/rerank.gguf
      expand: hf:user/model/expand.gguf
      gen: hf:user/model/gen.gguf

Model URIs support:

  • hf:org/repo/file.gguf - Hugging Face download
  • file:/path/to/model.gguf - Local file
  • http://host:port/path#modelname - Remote HTTP endpoint (OpenAI-compatible)

Download Policy

Model provisioning follows one of three modes:

  • default: auto-download allowed on first use
  • offline: cached models only (HF_HUB_OFFLINE=1 or GNO_OFFLINE=1)
  • manual: no auto-download, but explicit gno models pull still works (GNO_NO_AUTO_DOWNLOAD=1)

The dashboard bootstrap panel reflects the active mode in plain language. Starting serve or daemon does not resolve or download models. An empty or offline model cache does not block listener startup, lexical search or metadata access. The first operation requiring inference, including background embedding, resolves its model under the policy captured for that context. Missing models or blocked downloads follow the operation's existing error or fallback contract.

Using A Fine-Tuned Local Model

Fine-tuned expansion models can be paired with a separate answer model via a custom preset:

models:
  activePreset: slim-tuned
  presets:
    - id: slim-tuned
      name: GNO Slim Tuned
      embed: hf:Qwen/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf
      rerank: hf:ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF/qwen3-reranker-0.6b-q8_0.gguf
      expand: hf:guiltylemon/gno-expansion-slim-retrieval-v1/gno-expansion-auto-entity-lock-default-mix-lr95-f16.gguf
      gen: hf:unsloth/Qwen3-1.7B-GGUF/Qwen3-1.7B-Q4_K_M.gguf

Notes:

  • training backend may be Mac-only (for example MLX LoRA on Apple Silicon)
  • exported artifacts remain portable if you fuse and convert to GGUF
  • keep embed/rerank unchanged unless you have benchmark evidence for changing them too
  • expand drives retrieval-time query expansion
  • gen drives standalone answer generation (gno ask --answer, Web Ask)

See Fine-Tuned Models for the full workflow and troubleshooting notes.

HTTP Endpoints

GNO supports remote model servers using OpenAI-compatible APIs. This allows offloading inference to a more powerful machine (e.g., a GPU server on your network).

Remote endpoints receive the text sent to their configured role: queries and document chunks for embedding/reranking, generated expansion input for expand, or retrieved answer context for gen. Use HTTPS and server-side access controls outside a trusted network; remote inference is not part of the local privacy boundary.

Every inference port requires an explicit participating-collection scope; corpus-wide use is represented explicitly rather than inferred from an omitted filter. An empty or unknown scope fails before DNS. For a valid scope, GNO first performs bounded DNS-only classification without sending HTTP headers, request bodies, credentials, or model metadata, then intersects the proven endpoint zone with every participating collection's policy. local_only permits loopback model servers, lan permits private-address literals and hostnames whose complete DNS answer is homogeneously private, and remote permits pinned HTTPS public providers. DNS answers are pinned and rechecked before connection. Mixed, public-for-LAN, special-use, or rebound answers fail before request transfer. Redirects must remain on the same origin and are re-evaluated at every hop; credentials and request bodies are never forwarded cross-origin.

models:
  activePreset: remote
  presets:
    - id: remote
      name: Remote GPU Server
      embed: "http://192.168.1.100:8081/v1/embeddings#qwen3-embedding-0.6b"
      rerank: "http://192.168.1.100:8082/v1/completions#qwen3-reranker"
      expand: "http://192.168.1.100:8083/v1/chat/completions#gno-expand"
      gen: "http://192.168.1.100:8083/v1/chat/completions#qwen3-4b"

URI Format: http://host:port/path#modelname

ComponentDescription
http(s)://Protocol (HTTP or HTTPS)
host:portServer address
/pathAPI endpoint (e.g., /v1/chat/completions)
#modelnameOptional model identifier sent in requests

Supported Endpoints:

Model TypeAPI PathOpenAI-Compatible API
embed/v1/embeddingsEmbeddings API
rerank/v1/completionsCompletions API (text only)
expand/v1/chat/completionsChat Completions API
gen/v1/chat/completionsChat Completions API

Example with llama.cpp server:

# Start llama-server for generation
llama-server -m model.gguf --host 0.0.0.0 --port 8083

# Configure GNO to use it
# gen: "http://192.168.1.100:8083/v1/chat/completions#my-model"

Benefits:

  • Offload inference to a GPU server
  • Share models across multiple machines
  • Use larger models than local hardware supports
  • Keep local machine responsive during inference

Timeouts

models:
  loadTimeout: 60000 # Model load timeout (ms)
  inferenceTimeout: 30000 # Inference timeout (ms)
  expandContextSize: 2048 # Context window used for query expansion generation
  warmModelTtl: 300000 # Keep-warm duration (ms)

loadTimeout and inferenceTimeout accept integer milliseconds from 1 through 2,147,483,647; zero, negative, fractional and overflowing values fail configuration validation. Neither setting has a disabled-by-zero mode.

inferenceTimeout starts at native evaluation, after model/context loading. The child reports evaluation start using the current generation and request ID; metadata and queued requests do not start that timer. loadTimeout bounds the dispatched request's loading phase independently; the two timers are never added. A caller's absolute deadlineAt covers queueing and loading through response publication. Generation receives an evaluation abort signal. Embedding/reranking evaluation may be noncooperative, so canceled active work retains capacity until actual settlement. After five seconds without settlement, the parent retires its isolated child; queued operations fail explicitly and are never replayed.

For remote HTTP inference, inferenceTimeout measures the complete request from policy/DNS preparation through fetch and response-body consumption. The client cannot observe the remote model's load/evaluation boundary. The caller's deadlineAt remains the tighter outer limit. loadTimeout applies only to local native loading; it does not configure a remote server.

The separate expansion-stage budget still falls back without expansion when it expires, while aborting its generation. A caller abort/deadline cannot become a successful lexical fallback. Operational cancellation options do not change sampling, model selection, candidate counts, or ranking.

The resident shutdown contract allocates a shared five-second drain, five-second abort-settlement period, then at most one second awaiting forced owned-child exit. Requests, accepted jobs, background scheduling and listener cleanup share those phases; individual participants do not restart the clock. Admission and scheduling stop first. At the abort deadline, suspended parent transactions are rolled back and their store access revoked before the database closes. Completed checkpoints remain durable; unfinished embedding work stays pending for restart. Only the owned native child can be forced to exit; an unconfirmed OS termination is reported as an error, never successful cleanup. See daemon shutdown.

These bounds apply while the parent event loop can run. JavaScript timers cannot preempt a synchronous callback, a blocked OS call or a SQLite statement already executing when the signal arrives. During shutdown, ordinary store access caps SQLite busy waiting to the remaining settlement budget. Raw database handles cached outside the store API do not inherit that per-access cap or transaction token; they become unusable when their connection closes. The bounds are not a universal query latency promise and have no new CLI/configuration setting.

Native embedding, generation and reranking share one child per LLM adapter. warmModelTtl is the inactivity grace for each model and for the native child (five minutes by default). Model-specific leases protect actual loading, context creation, evaluation and cleanup. Background embedding does not renew idle generation/reranking weights; they can expire while the same child remains busy. A later call reloads the expired model and rebinds its context. Cached metadata and status reads do not renew a model's idle grace. Active inference, pending responses and model-use leases prevent idle retirement; metadata-only access does not refresh native activity. Retirement exits the child to reclaim its native process allocations. The next inference reloads models. That complete cold request includes worker startup, model loading and context creation; its cost depends on the model, backend and hardware. Changing the idle grace does not change candidate counts, precision or input text. Command/SDK disposal terminates the owned child. Explicit model disposal retires the shared child, so other native roles reload lazily too. HTTP inference is unaffected by this child lifecycle.

Background embedding runs in internal turns of at most 32 pending chunks. A scheduler run or accepted job may span many turns; it does not report completion after one page. Each turn checkpoints current inputs, releases native/model ownership and yields before continuing. Foreground requests dispatch first, with one pending background native request served after at most eight completed foreground native inference dispatches. Metadata init/dispose do not earn or reset that service credit. Both classes share the existing 64-call waiting queue. Native batches are not preemptible and this is not a total-query deadline. Failed chunks remain pending for a subsequent pass; later pages still progress. Notifications keep the existing 30-second debounce and five-minute maximum wait, and failed scheduler passes retry after debounce. No priority or turn-size configuration is exposed.

Resident model counters are cached child-reported lifecycle snapshots. Reading status sends no native request and does not extend the idle deadline. Loaded-model counts describe residency, not GPU-memory usage; capability flags describe configured functionality.

Native worker startup or inference failure returns a structured error without automatically replaying the operation. The npm package and desktop source runtime include the worker entrypoint. A standalone bun build --compile executable without that source runtime cannot launch native inference and fails explicitly instead of recursively launching itself.

FTS Tokenizer

Set at gno init, cannot be changed without rebuilding.

TokenizerDescription
snowball englishEnglish Snowball stemmer (default)
unicode61Unicode-aware, no stemming
porterEnglish-only stemming (legacy)
trigramSubstring matching

The exposed Snowball tokenizer is specifically snowball english; it enables English word-form matching such as "running" → "run" and "scored" → "score". Use unicode61 for language-neutral Unicode tokenization without stemming.

# Initialize with unicode61 (no stemming)
gno init --tokenizer unicode61

SQLite busy timeout

busyTimeoutMs sets SQLite's busy_timeout pragma: how long a connection waits for a lock before failing with SQLITE_BUSY. The default is 60000 (60 seconds). Valid values are integers from 1000 to 600000 milliseconds.

Raise this when embedding passes run for tens of seconds to minutes on slow disks, so a brief overlap with another writer queues instead of failing in 5 seconds. Invalid values are rejected at config load.

busyTimeoutMs: 120000

Inspect the live value (the pragma on the open database, not the config echo) with gno doctor or gno doctor --json.

Environment Variables

Override paths (applied before platform defaults):

VariableDescription
GNO_CONFIG_DIROverride config directory
GNO_DATA_DIROverride database directory
GNO_CACHE_DIROverride model cache

Runtime/model env vars:

VariableDescription
GNO_LLAMA_GPULocal llama backend: auto, metal, vulkan, cuda, or CPU off
NODE_LLAMA_CPP_GPUCompatibility alias used when GNO_LLAMA_GPU is unset
GNO_LLAMA_BUILDBackend build mode: default never; set autoAttempt to opt in
GNO_LLAMA_INIT_TIMEOUT_MSBackend initialization timeout; default 30000 ms
GNO_EMBED_CONTEXTSOverride CPU embedding context count, clamped to 1-4
GNO_EMBED_CONTEXT_SIZEOverride native embedding context size; minimum 128
GNO_EMBED_THREADSOverride CPU threads per embedding context
GNO_NO_AUTO_DOWNLOADDisable automatic model downloads; explicit models pull allowed

On Windows CPU-only runs, GNO defaults to one embedding context below 16GB RAM, and at most two contexts from 16GB upward. Increase GNO_EMBED_CONTEXTS only when memory headroom is clear and a real benchmark shows a gain.

File Locations

Linux (XDG):

PathPurpose
~/.config/gno/index.ymlConfig
~/.local/share/gno/index-default.sqliteDatabase
~/.cache/gno/models/Model cache

macOS:

PathPurpose
~/Library/Application Support/gno/config/index.ymlConfig
~/Library/Application Support/gno/data/index-default.sqliteDatabase
~/Library/Caches/gno/models/Model cache

Run gno doctor to see resolved paths for your system.

Editing Config

Edit directly or use CLI:

# Add collection via CLI
gno collection add ~/notes --name notes

# View config (Linux)
cat ~/.config/gno/index.yml

# View config (macOS)
cat ~/Library/Application\ Support/gno/config/index.yml

After manual edits, run gno update to apply changes.