Configure GNO via user config or a portable project profile, manage collections, and pick the right model preset.
Compiled artifacts add no background refresh or instruction edits. Current index, tokenizer identity, and egress rules still govern. Read the compiled context guide for limits, indexed freshness, inclusion, and recovery.
Change collections and presets from the CLI or Web UI. The YAML file is for defaults the UI does not cover. User config is ~/.config/gno/config/index.yml (Windows: %APPDATA%\gno\config\index.yml). gno doctor prints the resolved paths.
~/.config/gno/config/index.yml%APPDATA%\gno\config\index.ymlRun gno doctor to see the resolved paths for your machine.
busyTimeoutMs sets SQLite busy_timeout for the index database. Integer milliseconds from 1000 to 600000; default 60000. gno doctor reports the live PRAGMA busy_timeout value. Raise it for long embedding passes on slow disks.
busyTimeoutMs: 60000Writes sent with a request ID are recorded in a private per-index ledger under write-receipts/ in the data directory. It has no configuration settings; see Retry-safe writes for location, retention, and capacity.
Optional chunking settings apply to one index. Omitted settings retain the current defaults of 800 approximate tokens and 15% overlap. Upgrading with omitted or explicit defaults does not rechunk or re-embed unchanged documents.
chunking:
maxTokens: 256
overlapPercent: 0maxTokens is an integer of at least 10. GNO estimates four characters per token; it does not count exact embedding-model tokens. overlapPercent accepts a fraction from 0 through 0.5: use 0.15 for 15%, not 15. Each missing field inherits its default. Invalid settings fail before indexing.
After a policy change, run gno index to rechunk and embed. gno update or gno index --no-embed rechunks cached Markdown without generating embeddings; follow with gno embed for semantic search. Changed chunks lose obsolete embeddings, while identical chunks retain theirs.
Rechunking does not reread or edit original files. It covers the whole index even during a collection-scoped or targeted sync, while source refresh and collection-scoped embedding keep their usual scope. Use unscoped gno index to finish the whole index. Cached layout readiness does not prove source freshness.
gno status --json reports configured and applied parameters, layout state, and pending document/mirror counts under chunking. Empty or mixed layouts have applied: null. legacy-default means the existing layout is current without an eager backfill. Check source errors and embeddingBacklog separately. Interrupted mirror work stays pending for the next indexing run.
On CHUNKING_POLICY_CONFLICT, reopen the client or restart its resident runtime with the intended config. A stale client cannot overwrite a newer policy. There are no per-collection chunking overrides or temporary update flags.
Create research-256.yml below, replacing the corpus path. Copy it to research-800.yml and change only maxTokens to 800.
version: "1.0"
collections:
- name: research
path: /absolute/path/to/research
pattern: "**/*.md"
chunking:
maxTokens: 256
overlapPercent: 0.15gno --config ./research-256.yml --index research-256 index --no-embed
gno --config ./research-800.yml --index research-800 index --no-embed
gno --config ./research-256.yml --index research-256 status --json
gno --config ./research-800.yml --index research-800 status --jsonFor vector/hybrid comparisons, use the same cached embedding model in both configs and run gno embed with each matching --config and --index. Check that pending chunking counts and embedding backlog are zero. Save configs, GNO/model versions, corpus hashes, query sets, and results.
BM25 ranks whole documents, so chunk size alone can leave lexical ranking unchanged. Chunk boundaries affect semantic retrieval and selected evidence spans. Hold type boosts and other ranking settings constant when testing structure. No single chunk size is best for every corpus.
Each file GNO indexes has a budget covering its conversion to Markdown and the processing after it (metadata, code detection, change journal, chunking and link extraction):
conversion:
timeoutMs: 120000 # per file; default 60000 (60 s)
maxMemoryMb: 8192 # default: half of physical memory, at least 2048timeoutMs is the wall-clock time one file may take, in whole milliseconds from 1000 through 86400000. maxMemoryMb is the resident memory the GNO process may reach while a file is processed, in whole MB from 256 through 1048576. It measures the whole process, not just that file.
A file that runs past either limit is stopped. GNO records it with the error code TIMEOUT or MEMORY_LIMIT, leaves it unindexed and goes on with the rest of the collection. The next gno update or gno index (or resident sync) tries the file again. If a file keeps being stopped, raise the limit that stopped it, or exclude the file.
What is enforced, and how:
/proc on Linux and ps on macOS; on Windows the child is held to the time limit only). If neither can start, the file fails with ISOLATION_UNAVAILABLE rather than being processed without its budget. When GNO stops (Ctrl-C, SIGTERM, a resident shutting down, or a normal exit) it kills that child with it. On Linux the child also dies if GNO is killed outright (SIGKILL); on other systems such a child stops at the end of its current step.While a file is still being processed after 10 seconds, or after half its budget if that is sooner, gno update and gno index name it on stderr:
Still converting work/reports/pivot-report.xlsx after 10s (budget 60s)gno update output, gno status (under “Stopped at conversion budget”) and the gno audit finding freshness.index-revision name a stopped file and its code.
A repository can commit a portable .gno/index.yml profile that describes one collection, its contexts and content types, and request-local project-affinity defaults. A profile is optional: repos without one keep the normal user config and current-directory behavior.
schemaVersion: "1.0"
collection:
name: project-docs
root: docs
include: ["**/*.md"]
exclude: [node_modules, .git]
contexts:
- file: AGENTS.md
contentTypes:
people:
prefixes: [people]
preset: person
affinityDefaults:
enabled: true
contribution: 0.03Paths are repository-relative and cannot escape the selected project root. The schema is closed: unknown keys, absolute paths, traversal, environment expansion, Windows-reserved names, unsafe glob syntax, duplicate IDs, and references to GNO runtime files fail validation. Likely secret context paths such as .env, private keys, and credentials are rejected. Context files must be regular UTF-8 files no larger than 64 KiB; the profile itself is capped at 1 MiB. GNO also excludes .gno from the declared collection. Profile excludes containing glob characters are glob patterns; plain values match directory names. Multiple include globs preserve literal commas and bracket classes. Brace alternatives are rejected; express their branches as separate include or exclude entries.
gno profile check
gno profile show
gno profile diff
gno profile applyThe first three commands are read-only. apply adds or updates only what the profile declares. It does not index documents or delete other collections, documents, contexts, content types, or index state. Multiple contexts for one collection are preserved. GNO keeps a local record of the applied profile in your user config, so it can still see entries you later remove from the profile; receipts never include machine paths. The tracked profile is never rewritten. Config, data, database, model cache, selected file-backed models, apply receipt, and lock state must stay in the user’s external GNO runtime directories, never inside the repository. Overlap fails before mutation. Commit the portable profile and referenced guidance, not databases, model files, caches, locks, receipts, or secrets.
Each collection is a named source of documents. Collections have their own include/exclude rules and can optionally override model presets.
# Add a collection
gno collection add ~/notes --name notes
# Add with a glob pattern
gno collection add ~/code --name code --pattern "**/*.{ts,md}"
# List collections
gno collection list
# Remove a collection
gno collection remove notesOpening an index never deletes a collection. If the index holds documents of a collection your config does not list (you removed it from the config file by hand, or a second --config points at your normal index), commands refuse with a message that names those collections and change nothing. gno status lists them under Collections not in this config (JSON: staleCollections). Add the collection back, give the other config its own index with --index <name>, or run gno cleanup with your usual config to delete its index data. A running gno serve or gno daemon keeps the documents of a collection removed by hand and refuses requests the same way until you do one of those. gno collection remove deletes a collection's documents at once.
A collection can run an updateCmd (for example git pull) in its folder before each gno update or gno index. The command stops, together with anything it started, when gno exits or is interrupted, and after updateCmdTimeoutMs milliseconds (10 minutes by default), so a hung git or rsync never keeps running on its own. A command that ignores the stop request is ended 5 seconds later.
collections:
- name: wiki
path: /Users/you/wiki
updateCmd: "git pull"
updateCmdTimeoutMs: 120000If you index one Obsidian vault as several collections (for example one per top-level folder), a plain [[Note]] in one collection finds its target in another, the way Obsidian does. Collections whose folders share one link workspace resolve plain wiki links across each other. GNO finds the workspace for you: the nearest folder, at or above the collection folder, that contains an .obsidian folder. A collection outside any vault keeps its links to itself, as before.
collections:
# Join a workspace without an .obsidian folder (Logseq, Foam, Dendron, plain folders)
- name: projects
path: /Users/you/notes/projects
workspaceRoot: /Users/you/notes
# Keep this collection's links inside it, even in a vault
- name: client-x
path: /Users/you/vault/Clients/X
workspaceRoot: falseworkspaceRoot must be an absolute path to an existing folder that contains the collection folder; anything else stops GNO with an error naming the collection. A vault nested inside another vault is its own workspace. gno status and gno collection list show each collection's link workspace and whether it was detected or configured, and the next gno update after a workspace change rebuilds the link graph and says so.
Inside a workspace, a target with / such as [[Spaces/AI/Agents/_index]] matches a file by its path from the vault root (then from the collection root, then by path suffix); a plain name matches a file name. When several files match, GNO prefers an exact path, then a file in the linking note's folder, then the file with the fewest folders above it. Files still tied are listed by gno audit links and create no graph link. [[collection:Note]] still names one collection explicitly. Inside a Markdown table, Obsidian's escaped alias [[Note\|Alias]] links to Note.
Obsidian also resolves links to files GNO does not index: pasted images and PDFs such as ![[diagram.png]], notes in folders no collection indexes or a collection excludes, and files a Markdown link names by path, such as [doctor.sh](scripts/doctor.sh) in a collection that indexes only **/*.md. gno audit links checks only that such a file exists in the vault and reports the link as outside-index, an informational finding that does not fail the audit. GNO does not index, open, or return the file, and the link does not join the link graph. A file other than a note needs its extension in the link; .md is optional for notes; a Markdown link must name the file's path relative to the note, and for this existence check .md is optional there too: [plan](Archive/Plan) finds an unindexed Archive/Plan.md. A Markdown link without .md to an indexed note is still reported as unresolved, so write the .md for links you want in the link graph.
Like Obsidian, GNO reads links only in Markdown notes and only outside code. Link-shaped text in an inline code span such as `[[Note]]`, a fenced code block, or an indented code block is not a link, and neither is link-shaped text in other indexed files, such as Python scripts, JSON or text files, or converted PDF and Office documents in a collection that indexes **/*. Those files stay searchable; they just have no links, backlinks, or link findings.
Markdown links follow CommonMark, as Obsidian renders them. A [ escaped with a backslash, as in quoted prose such as \[clause](see section 4), opens no link, and a link destination with an unescaped space is plain text. To link to a note whose name has a space, write [x](<my note.md>) or [x](my%20note.md). A link title such as [x](note.md "Title") is not part of the target.
Resolving a link never widens what you get back. A search, answer, Context Capsule, backlink list, or impact run scoped to some collections returns documents from those collections only, and a document outside the scope is never used as a stepping stone. Collection egress policies apply unchanged.
Every collection has one effective boundary: local_only, lan, or remote. An omitted value fails closed to local_only. Collections created before this policy was introduced keep their indexed documents and lexical/vector data, but report legacy_default provenance; newly synchronized collections without an explicit value report config_default. Neither default permits LAN or remote transfer. Egress policy is distinct from source availability (below).
local_only permits local files, processes, loopback clients, and loopback model servers only.lan additionally permits authenticated, proven private-network peers.remote additionally permits authenticated public transport and pinned HTTPS model providers.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_onlyAuthentication, write permission, and collection policy are independent gates. Mixed or derived evidence uses the most restrictive participating collection. Relaxation requires the current single-use policy revision; tightening invalidates resident sessions, active streams, queued jobs, and saved authorization state. Tightening blocks future GNO-controlled transfers but cannot recall data already uploaded. Unpublish or delete the hosted item separately in Studio, including public publications. Unpublish retains source and history; deletion denies access before background cleanup. Independent copies and your local files remain. Encrypted artifacts stay client-encrypted; gno.sh never receives the passphrase and cannot decrypt or recover them.
collections[].sourceAvailability is optional and independent of egressPolicy. Exact values: any (default) or local. Availability controls whether source content may be materialized during indexing; egress controls where derived data may travel. There is no separate public knob beyond these two modes.
any reads every matching file, which can make a cloud provider download files that are not stored locally.local is opt-in for macOS cloud folders. It indexes only files already stored on the Mac, skips cloud placeholders without downloading them, and refuses to run on unsupported platforms or when the check cannot be set up.In either mode, a collection folder that is missing during a sync (an unmounted drive, an offline sync folder, a network share that is down) is not treated as empty. Full syncs, targeted syncs and the watcher keep that collection's documents and report SOURCE_AVAILABILITY_UNKNOWN with the path and the original error; gno cleanup in the meantime keeps their content. A folder that exists but is empty still counts as empty. While the folder is away, gno status, gno doctor, and the Web UI name the collection and the error, and the report clears as soon as the folder is back. See Collection root could not be resolved.
Sync receipts distinguish eligible files, CLOUD_PLACEHOLDER / CLOUD_PARTIAL skips (not conversion errors), DATALESS_DIRECTORY, and fail-closed SOURCE_AVAILABILITY_* codes. Files already indexed under a folder GNO cannot check stay searchable; GNO does not treat them as deleted.
Supported: Google Drive, iCloud Drive, and OneDrive on macOS. For Google Drive, point the collection at My Drive or at one Shared drive (Shared drives/<drive>), or a folder inside either; the Shared drives folder itself is not supported, so use one collection per Shared drive. For SharePoint libraries synced through OneDrive, point the collection at a library folder directly under the SharedLibraries location, not at the parent folder or deeper subfolders (two libraries tested). Windows and Linux cloud folders are not supported. The provider may still update its own metadata. In local mode GNO never pins, evicts, or downloads files. Local mode adds about 1% to folder scan time.
collections:
- name: drive-notes
path: /Users/you/Library/CloudStorage/GoogleDrive-…/My Drive/notes
pattern: "**/*"
sourceAvailability: local
egressPolicy: local_only
- name: team-drive
path: /Users/you/Library/CloudStorage/GoogleDrive-…/Shared drives/Team
pattern: "**/*"
sourceAvailability: local
egressPolicy: local_onlyIn a Shared drive, files you have never opened on this Mac stay cloud-only. GNO indexes the files that are already local and reports the rest as CLOUD_PLACEHOLDER.
GNO streams user-controlled exports into separate searchable records: one JSONL object, mail message, calendar event, transcript cue, or browser item at a time. Each record retains its real export path, exact locator, dates, people, available thread/event/session identity, attachment inventory, and anchors across search, get, Ask, and Context Capsules.
To see the Markdown GNO stores for one document (Markdown, text, PDF, or Office), run gno convert; it needs no collection or index. The export files below hold several records, so gno convert refuses them: index them in a collection instead.
.jsonl, .ndjson, .eml, .mbox, .ics, .vtt, .srt, and explicit .browser-export files.recordAdapters.transcript.format; GNO never guesses.collections:
- name: exports
path: /Users/me/exports
pattern: "**/*"
include: [.jsonl, .eml, .mbox, .ics, .vtt, .srt, .browser-export]
recordAdapters:
jsonl:
fieldMapping:
id: /external_id
body: /payload/text
participants: /participants
dateFields:
created: /created_atReimport with the same stable identity updates in place. Only a fully valid complete snapshot can deactivate disappeared records; malformed, truncated, or cap-limited snapshots preserve unseen records. Defaults cap a container at 100 MiB, one record at 2 million canonical characters, a snapshot at 50 million characters or 100,000 records, retained failures at 1,000, and adapter iteration at 60 seconds. Calendar recurrence emits at most 64 bounded local anchors. Partial imports always produce a visible warning; verbose output adds stable codes, locators, retryability, and redacted messages.
JSONL updates preserve identity only when a configured mapping or a conventional id field supplies one. Without either, GNO derives identity from canonical row content, so editing a row appears as one removed record plus one added record rather than an in-place update.
Export adapters never authenticate to mail/calendar/browser accounts, inspect live browser profiles, cookies, passwords, or sessions, fetch URLs or attachments, execute embedded content, or unpack archives. HTML becomes inert text. In structured results, source.relPath is the real export container; use the unique uri or docid to retrieve one record. The bounded record.adapter object identifies the exact adapter version and configuration fingerprint. .gno/records/ is reserved for virtual record identities and never walked as physical collection content.
Add an exclude array to a collection to skip files and directories:
collections:
notes:
path: ~/notes
exclude:
- node_modules
- .git
- "**/*.tmp"projectAffinity.enabled defaults to true and controls only the user-configured cwd fallback. projectAffinity.contribution defaults to and is capped at 0.03. For a local CLI request, repeatable explicit --project-root values win; otherwise the nearest valid.gno/index.yml supplies its canonical project root and request-local affinity settings. Missing or invalid profiles fall back to the cwd-derived root and user default. An explicit root or valid profile therefore still applies when the user-config fallback is disabled. --no-project-affinity disables all three sources for that request. Explicit roots replace inferred roots, duplicate or overlapping roots never stack, and all auxiliary scoring shares one ±0.08 cap. Remote hints are a separate untrusted input: they never trigger local profile or filesystem discovery and currently contribute zero affinity. Collection, tag, date, exclude, and egress filters remain hard.
GNO ships four built-in presets. Switch with one command; the first pull downloads the model files and caches them locally.
All four built-in presets use Qwen3-Embedding-0.6B-GGUF for embeddings. Model cache size depends on the selected files, quantization, and what is already cached.
gno models use balanced
gno models pull
gno models listcontentTypes is an optional schema-lite layer for second-brain pages. Without rules, nothing changes. When a rule matches, GNO indexes canonical contentType metadata alongside normal category filters.
contentTypes:
- id: person
prefixes: [people/, contacts/]
preset: person
- id: meeting
prefixes: [meetings/]
preset: meeting
searchBoost: 1.15
temporal: trueFrontmatter type becomes canonical only when it matches a configured content type ID. Otherwise it remains category metadata. A canonical configured frontmatter type wins over longest-prefix matching; only one rule applies, so overlapping prefixes cannot stack.graphHints types projected graph relationships.
searchBoost accepts 0.5 to 2; omitted or 1 is exactly neutral. One matched rule contributes at most ±0.05 and composes once with trusted local project affinity under a shared ±0.08 auxiliary cap. BM25 and vector boosts only adjust already-selected candidates: they never widen candidate retrieval or defer minScore. In hybrid search, boosts cannot override the reranker’s order or displace the top keyword match. Final scores stay in 0..1. No boost bypasses collection, tag, date, category, author, or exclude filters.
Query and Ask --explain receipts show the base score, factor, bounded contribution, combined cap, final score, rule source, and full ranking-rules fingerprint when a non-neutral rule is active. Status exposes only rule IDs, normalized factors, and that fingerprint; configured prefixes stay private. Boost-only config edits change the ranking fingerprint without requiring document conversion or vector rebuilding.
Override embed, rerank, expand, or generation models per collection. Example: a code collection on a code embedding model, a notes collection on the preset embed model.
collections:
code:
path: ~/code
models:
embed: nomic-ai/nomic-embed-code-v1.5Point GNO at an OpenAI-compatible server (Ollama, LM Studio, vLLM) running on another machine. Remote roles receive the query, chunk, or retrieved answer context sent to that role; they do not receive the corpus as a remotely mounted index.
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-4bmodels:
loadTimeout: 60000
inferenceTimeout: 30000
expandContextSize: 2048
warmModelTtl: 300000Timeout values are integer milliseconds from 1 through 2,147,483,647; zero does not disable either timeout. For local inference, loadTimeout bounds dispatched model/context loading and inferenceTimeout starts at actual native evaluation, after loading. These independent timers are not added. For HTTP inference, the inference budget covers policy and DNS preparation, fetch, and response-body consumption; local load timeout does not configure the remote server.
SDK deadlineAt is an absolute Unix epoch time in milliseconds covering queueing, loading, evaluation, and response publication. Caller cancellation or deadline expiry suppresses late success, including lexical fallback. The separate expansion-stage budget can still return a result without expansion; raising a model timeout does not extend that stage budget.
models.warmModelTtl is how long an idle local model stays loaded, five minutes by default. Running work keeps a model loaded; status and metadata reads do not. Background embedding does not keep idle generation or reranking models loaded. The next call after the grace period reloads the models, so it is slower. A longer grace uses more memory in exchange for fewer cold loads. It does not change search results.
GNO uses node-llama-cpp for local GGUF models. The default path uses prebuilt backends only; source builds are opt-in so normal indexing does not unexpectedly require local compiler toolchains. Local models run in a separate Bun process that GNO starts and stops for you; HTTP model servers are called directly. Use the npm package or the desktop app: a standalone compiled executable cannot run local models and fails with an error.
GNO_LLAMA_GPU — choose auto, true, false, cuda, vulkan, or metal. NODE_LLAMA_CPP_GPU remains a compatibility alias when this is unset. Switching backend keeps your existing embeddings when a sample of stored chunks produces the same vectors on the new backend (see Switching backend or Bun version).GNO_LLAMA_BUILD — backend build mode. Default: never. Set autoAttempt only when you intentionally want node-llama-cpp to try a local source build.GNO_LLAMA_INIT_TIMEOUT_MS — local backend initialization timeout. Default: 30000.GNO_EMBED_CONTEXTS — override CPU embedding context count, clamped from 1 to 4. CPU-only runs choose a small adaptive pool automatically: one context on low-memory Windows machines, otherwise at most two contexts unless you explicitly override it.GNO_EMBED_THREADS — override CPU threads per embedding context.GNO_EMBED_CONTEXT_SIZE — override native embedding context size. Minimum: 128.GNO_NO_AUTO_DOWNLOAD — disable automatic model downloads; explicit gno models pull still works.Opt in per document with a nested YAML gno.metadata mapping. No model or collection-wide schema is required. Normal sync repairs extraction on older indexes without re-embedding unchanged text.
Read the metadata guide: examples, operators, missing fields, limits, and recovery.