Skip to content

Latest commit

 

History

History
304 lines (221 loc) · 10.5 KB

File metadata and controls

304 lines (221 loc) · 10.5 KB
title Daemon Mode
description Run GNO as a headless watcher process for continuous local indexing without the web UI or desktop shell.
keywords gno daemon, continuous indexing, local watcher, headless indexing, background knowledge index

Daemon Mode

Run GNO as a headless long-running watcher process.

What It Does

gno daemon keeps the same watch/sync/embed loop alive without starting the Web UI. The same resident process also hosts stateful Streamable HTTP MCP at /mcp plus the redacted /api/resident/status lifecycle surface. Full /api/status, which includes local index and configuration details, is available only when the daemon binds to loopback.

The shared resident watcher treats exact and ambiguous notifications differently. A contained eligible file path always reaches content hashing, including same-size edits whose mtime was restored. Atomic-save temp names, missing filenames, directories, vanished paths, and recursive deletion events trigger bounded reconciliation against filesystem and active-index evidence. Only proven candidates are resubmitted and only proven removals are inactivated, so untouched siblings are preserved. Failed scans, queries, edge projection, or file sync retain durable retry authority; bounded overflow or a platform without native anchored directory handles escalates to a full collection sync.

These guarantees are exercised on supported local filesystems across macOS, Linux, and Windows. They do not claim universal watcher semantics for network, removable, or coarse-timestamp filesystems.

When a collection sets sourceAvailability: local, scheduled indexing and watch-triggered ingestion use the same source-availability boundary as foreground gno index / gno update: hierarchical directory classification, guarded content rechecks, cloud-placeholder skips, and preservation of indexed descendants under unproven prefixes. Default any is unchanged. Source availability is distinct from egressPolicy. Support is evidence-qualified for tested macOS File Provider configurations only (Google Drive, iCloud Drive, and OneDrive for both validated immediate SharePoint library roots); unsupported platforms/filesystems fail closed under local.

It also owns saved Context Capsule reverification. Register a Capsule with gno context watch <file>; after filesystem sync and embedding work settles, the daemon coalesces raw document-journal changes and reverifies affected evidence in one bounded serial batch. A durable journal high-water mark avoids duplicate work across restarts. If retention has expired that cursor, the next settled cycle conservatively checks all saved registrations.

Registrations persist metadata and evidence hashes only—never Capsule bytes, passage text, questions in notifications, or generated answers. --notify emits a metadata-only local capsule-reverified event after the verification record commits.

Each registration belongs to the index database named by its Capsule. Saved files remain caller-owned and byte-for-byte unchanged on success, missing-file, invalid-file, or exact-hash-change outcomes. A completed operation stores the canonical context verify receipt; a failed operation stores only its disjoint error record. Neither path invokes verified Ask or any generation model.

Use it when:

  • you want continuous indexing from the terminal
  • local agent workflows need a fresh index
  • you do not need the browser or desktop shell open

Start

In the foreground:

gno daemon

Stop with Ctrl+C.

Detached (background, macOS/Linux only):

gno daemon --detach

The parent prints PID <pid> and exits 0; the child writes to {data}/daemon.log (where {data} is resolveDirs().data, configurable via GNO_DATA_DIR). Override with --log-file <path>.

Skip Initial Sync

gno daemon --no-sync-on-start

That starts the watcher immediately and only reacts to future file changes. It does not weaken reconciliation for those future changes or change the status, Capsule-settlement, REST, or MCP contracts.

Managing the Daemon

gno daemon ships with built-in lifecycle controls. The contract mirrors gno serve exactly.

Flag Purpose
--detach Self-spawn a detached child; parent prints pid and exits 0
--status Read pid-file, check liveness, print status (--json for machine output)
--stop SIGTERM with 10s timeout, SIGKILL fallback
--pid-file <path> Override pid-file location (defaults to {data}/daemon.pid)
--log-file <path> Override log-file location (append-only)

--detach, --status, and --stop are mutually exclusive.

# Start detached
gno daemon --detach

# Check status (terminal)
gno daemon --status

# Check status (machine-readable; exits 3 when not running)
gno daemon --status --json

# Stop gracefully (SIGTERM with 10s timeout, then SIGKILL fallback)
gno daemon --stop

# Override paths
gno daemon --detach --pid-file /tmp/gd.pid --log-file /tmp/gd.log

Exit Codes

  • 0--detach succeeded, --stop completed, or --status found a live process
  • 1 (VALIDATION) — mutex violation, bad flag combination, Windows --detach, or --json paired with anything other than --status
  • 2 (RUNTIME) — runtime failure (IO/DB/model)
  • 3 (NOT_RUNNING) — --status or --stop found no live matching process

--status always emits the schema-shaped payload on stdout, even when it exits 3. --stop is silent when there is nothing to stop — script against the exit code, not stderr text.

For a live daemon, JSON status best-effort includes the same redacted resident snapshot available at GET /api/resident/status; no paths, tokens, queries, document content, or caller identities enter that nested lifecycle object. For a non-loopback listener, the HTTP status route additionally requires the same exact Host/Origin checks and bearer authentication as /mcp, plus a compatible effective policy for every participating collection. Either control can deny access; neither overrides the other.

--json Gating

--json is only defined for --status. Combining it with --detach, --stop, or the foreground path returns a VALIDATION error:

--json is only supported with `gno daemon --status`

Live-Foreign Pids

If you upgrade gno while a detached daemon is still running, the new binary treats the live process as foreign and refuses to manage it. --stop errors with a VALIDATION exit telling you to terminate it manually:

gno daemon (pid 12345) is live but was started by gno 1.0.4; this binary is 1.1.0.
Refusing to signal pid 12345; terminate it manually and delete /path/to/daemon.pid.

--status --json reports running:false and emits a NOT_RUNNING envelope on stderr with details.foreign_live = { pid, recorded_version, current_version }.

When To Use daemon vs serve

  • gno serve: browser or desktop session, full local REST API, dashboard, /mcp, and live indexing
  • gno daemon: headless continuous indexing, /mcp, and redacted lifecycle status only

Only one resident owner may use a data directory. Starting the other mode against the same GNO_DATA_DIR fails with the current owner hint; stop the owner before switching modes.

Typical Flow

gno init ~/notes --name notes
gno index
gno daemon --detach
gno daemon --status

Then keep using normal CLI commands in another terminal:

gno search "meeting notes"
gno ask "what changed this week" --answer
gno ls

When you're done:

gno daemon --stop

Offline / Manual Model Policy

Use global flags and env vars exactly like the rest of GNO:

gno daemon --offline
GNO_NO_AUTO_DOWNLOAD=1 gno daemon
  • --offline uses cached models only
  • GNO_NO_AUTO_DOWNLOAD=1 disables automatic download while still allowing explicit gno models pull

Windows

Native --detach is not supported on Windows. The flag returns a clean VALIDATION error pointing you at WSL. --status / --stop / --pid-file / --log-file remain parseable but have nothing to manage without a detached child.

For Windows-native long-running deployment, run gno daemon under WSL or wrap the foreground process with a service supervisor (NSSM, sc.exe).

Troubleshooting

"Nothing updated"

Check:

  • the daemon is still running (gno daemon --status)
  • the changed file matches your collection pattern/include/exclude rules
  • you did not start it with --no-sync-on-start and then expect old files to be imported retroactively

"I changed config but nothing happened"

Restart the daemon. v1 reads config on startup.

gno daemon --stop
gno daemon --detach

"I also ran gno serve"

The second process fails closed because serve and daemon are mutually exclusive resident modes for one data directory.

Use:

  • gno serve for browser/desktop sessions
  • gno daemon for headless continuous indexing

Stop the running owner before switching:

gno serve --stop
gno daemon --detach

"pid-file exists but --status says not running"

The recorded pid is dead. --status reports stale pid-files as running:false (exit 3); the next --detach cleans the stale pid-file automatically before spawning the new child.

"another serve/daemon start is in progress"

Two parallel --detach invocations race for the same pid-file. Detach takes out an atomic start-lock (a .startlock sidecar next to the pid-file) for the duration of the spawn. If you see:

another gno daemon start is in progress (lock-file /path/to/daemon.pid.startlock)

…another --detach is mid-flight. Stale locks (>30s old) auto-recover; if you need to clear a fresh stuck lock, delete the .startlock sidecar manually.

"live-foreign pid: refusing to signal"

You upgraded gno while a detached daemon was still running. --stop will not SIGTERM the old process because it was started by a different binary version.

gno daemon (pid 12345) is live but was started by gno 1.0.4; this binary is 1.1.0.
Refusing to signal pid 12345; terminate it manually and delete /path/to/daemon.pid.

Resolve manually:

kill 12345
rm /path/to/daemon.pid
gno daemon --detach

The same metadata is exposed to JSON consumers: gno daemon --status --json returns running:false plus a NOT_RUNNING envelope on stderr carrying details.foreign_live = { pid, recorded_version, current_version }.