Guides

Verified setup & project profiles

Why setup refuses to report success until retrieval works, and how to commit portable retrieval intent to a repository without committing runtime state.

Two related ideas live on this page. The first is that activating a folder should end with proof, not optimism. The second is that a repository should be able to carry its own retrieval configuration, so the next person who clones it gets the same setup without a wiki page of instructions.

Setup that proves itself

gno setup ~/notes --name notes
gno setup ~/notes --no-semantic
gno setup ~/notes --connector cursor-mcp --connector codex-skill
gno setup ~/notes --json

The usual failure mode of a local search tool is a green checkmark over a broken index. Files were walked, rows were written, a status page says “indexed”, and every query returns nothing. You find out days later.

gno setup does not return until it has derived a bounded term from the corpus it just indexed and confirmed that lexical search returns the expected document for it. The proof comes from your content, not from a fixture, so it cannot pass on an empty or mis-walked collection. Rerunning is safe: the same canonical folder and collection are reused.

Semantic readiness is tracked as a separate, resumable concern. Embeddings may still be downloading or running when setup returns, and GNO will say so rather than blocking or pretending. --no-semantic records an explicit skip. A pending semantic stage never invalidates the lexical proof you already have.

The same activation result appears in gno status --json, gno doctor, /api/status, and the Web Health Center. Note the distinction from /api/health, which proves only that the server process is alive.

Connectors

Repeatable --connector flags install and check supported agent integrations as part of setup: Claude Code, Claude Desktop, Cursor, Codex, OpenCode, OpenClaw, and Hermes. Installation is install-once and its result composes alongside the unchanged setup result rather than replacing it.

One honesty note: GNO can prove an MCP target retrieves, because it can run a bounded read-only retrieval against it. It cannot prove a client actually loaded an installed skill, so that step reports what was written rather than claiming runtime success.

Project profiles

A profile is a .gno/index.yml file committed to a repository, declaring what that repository wants indexed and how. It holds intent only. GNO’s database, model cache, receipts, and runtime locks stay outside the repository, and validation rejects any attempt to point them inside it.

schemaVersion: "1.0"
collection:
  name: project-docs
  root: docs
  include:
    - "**/*.md"
    - "**/*.pdf"
  exclude:
    - generated
  languageHint: en
  modelPreset: slim-tuned
contexts:
  - file: AGENTS.md
  - text: Prefer primary project decisions.
contentTypes:
  people:
    prefixes: [people]
    preset: person
    graphHints: [works_at, mentions]
affinityDefaults:
  enabled: true
  contribution: 0.02
recommendedCapabilities:
  - workspace.read

Every path is repository-relative and portable across POSIX and Windows. .gno is always excluded from the declared collection.

Inspect before you apply

gno profile check
gno profile show
gno profile diff

All three are read-only. Discovery walks upward to the first Git boundary and picks the nearest profile; a nested monorepo profile shadows an ancestor and profiles are never merged, so what applies is always one file you can point at. A worktree .git file counts as a boundary. Pass an exact directory or .gno/index.yml path to disable upward fallback.

Remote SDK, REST, MCP, and Web inputs cannot trigger profile discovery. Nothing reachable over the network gets to make GNO probe your filesystem for config.

Applying

gno profile apply
gno setup . --apply-profile

Profiles are never applied implicitly. Plain gno setup detects a nearby profile, prints preview guidance, and changes nothing. Application is create/update-only: unrelated collections, documents, and contexts in your index survive untouched, and reapplying an unchanged profile is a no-op that does not advance timestamps.

--apply-profile fails closed. Once setup has discovered a valid profile for an explicit request, applying it is a prerequisite: if apply fails, throws, or cannot return a complete collection receipt, setup stops before the ordinary folder transaction and before any connector work. An inspection transport failure stops even earlier, leaving config, index, and store state unchanged. A late failure may have left durable create/update-only state; rerunning gno profile apply resumes idempotently and gives the detailed diagnostic.

--apply-profile cannot be combined with explicit --name or --exclude overrides, because a profile that silently loses to a flag is worse than an error. An invalid or absent profile never makes profiles mandatory: ordinary folder setup continues, and with --json you get status: completed_with_actions plus profile.apply: null saying the optional action did not run.

What validation rejects

A profile is a file other people can commit to a repository you clone, so the schema is deliberately narrow. Rejected: absolute paths, path traversal, environment expansion, runtime database/model/lock paths, secret fields, arbitrary hooks, and symlink escapes. Also rejected: Windows-reserved names, trailing dots or spaces, unbalanced or negated globs, and likely-secret context paths such as .env, private keys, and credential files, including when a safe-looking symlink resolves to one of those inside the repository.

Context files must be regular UTF-8 files of at most 64 KiB; .gno/index.yml itself is bounded at 1 MiB. Brace alternatives ({a,b}) are intentionally rejected so the runtime and the published schema enforce identical path rules; use separate include or exclude entries instead.

Project-aware ranking

affinityDefaults ties into GNO’s project affinity: when you run a trusted local CLI query from inside a repository, a matching collection receives one small, bounded, explainable ranking contribution (at most +0.03, sharing a ±0.08 cap with other auxiliary signals).

The boundaries matter more than the number. It is a soft tie-breaker, not a filter: it can reorder near-equals, never create candidates or override collection, tag, date, exclude, or egress filters, and a clear base-score lead still wins. --project-root sets it explicitly, --no-project-affinity turns it off, and --explain shows the contribution with redacted aliases rather than raw paths. SDK, REST, MCP, and Web projectHints are opaque untrusted metadata with zero ranking effect: those surfaces never probe a filesystem.