Rune Gurd docs

gurd scan

`guard scan` is Rune Guard's read-only, offline report of the AI agent configuration on a machine, and `guard assess` turns it into an explainable assessment.

What it does

guard scan runs once and works offline with no account. It runs nothing it scans and uses no network. It writes only to the Guard home: its package cache, its credential reference key and, with --guardrail-changes or --credential-attribution, their baselines.

guard scan                           # home directory and the current directory
guard scan --project ~/src/app --project ~/src/api
guard scan --discover                # recorded projects and the catalog's search roots
guard scan --no-project              # home directory and system locations only
guard scan --guardrail-changes       # risky settings new since the last baseline
guard scan --credential-attribution  # label each credential by who wrote it
guard scan --context-changes         # who added planted instructions to memory and rules
guard scan --no-cache                # analyse every package again, and don't write the cache

What it checks

Area What it reports
Harnesses Which harnesses have configuration here, per file and scope (managed, system, user, project or local).
Posture Settings that lower a harness's guardrails, and whether each is effective or overridden.
Content Secrets and hidden instructions in rules, memory, skill and hook files.
Credentials Static credentials in agent configuration, MCP configuration and project environment files.
MCP MCP servers, from the same report as guard mcp-inventory. See mcp.md.
Packages Installed plugins, extensions and skills, where they came from, and what their code does.
Privacy grants (macOS) Full Disk Access, Accessibility, Input Monitoring and screen recording permissions held by AI tools.

Posture

Each finding has the rule, harness, severity, file path and scope, the key and the risky value. A value that matches a secret pattern is left out. A value is not effective when a higher-precedence file overrides it (overridden_by) or the harness ignores it in that scope (not_honored_in_scope).

Harness Example checks
Claude Code permissions.defaultMode of bypassPermissions, Bash(*) in permissions.allow, sandbox.enabled: false, disableAllHooks
Codex approval_policy = "never", sandbox_mode of danger-full-access
Cursor CLI approvalMode: "unrestricted", Shell(*) allow entries
Gemini CLI tools.autoAccept, tools.sandbox: false
Copilot CLI disableAllHooks, storeTokenPlaintext
Windsurf a shell or * in windsurf.cascadeCommandsAllowList
Cline executeAllCommands, YOLO and approve-all mode

On Windows, guard scan also reads the policy registry keys the catalog names, such as Claude Code's HKLM\SOFTWARE\Policies\ClaudeCode, but not for --ci. Of Claude Code's managed sources only the highest present applies, and the user-writable HKCU policy counts only when no admin source exists.

A setting that is absent is not reported, even when its default is risky. Command-line flags such as --dangerously-skip-permissions or --yolo aren't in files, so guard scan can't see them. Hooks check them, and the sensor reports an agent CLI it sees start with one, or with a variable that moves its configuration directory.

When CLAUDE_CONFIG_DIR, CODEX_HOME, COPILOT_HOME or CURSOR_CONFIG_DIR is set to an absolute directory in the scan's own environment, guard scan also reads the settings, content and MCP files from that directory. Run the scan in the same environment as the CLI: a variable set only for the CLI's process isn't seen. Package registries such as ~/.claude/plugins/ are still read from the default directory only.

Recognized VS Code extension identities establish installation only. They do not prove execution, hook delivery or which model ran. Editor state databases, arbitrary extension settings and centrally managed VS Code AI policies are not read. Copilot, Cline, Continue, Kimi and Qwen have separate adapter limits; see Harness hooks.

Privacy grants

On macOS, privacy_grants lists the Full Disk Access, Accessibility and Input Monitoring grants held by AI tools the catalog names, and screen_recording lists their screen recording permissions. Both come from read-only queries of the system and user TCC databases, and cover grants made before the sensor started; the sensor reports grants made while it runs. A Full Disk Access grant is also a posture finding, full_disk_access_held_by_ai_tool, since it lets the tool read and change files macOS otherwise protects, such as mail and messages, without asking again.

Without Full Disk Access for the process running guard scan, macOS refuses the read: the database is not_available and its grants are unknown, not none. Terminals and editors that agents run in aren't listed, although agents started inside them inherit their grants. Tools are matched by bundle ID or executable name; TCC's code requirement isn't checked.

Content

Files are checked for secrets and injection markers, such as hidden Unicode characters, instructions in HTML comments, "ignore earlier instructions", and instructions to send credentials or switch off Guard. Memory, rules and skill files are also matched after normalization and base64 or hex decoding. Nothing from the file itself is reported.

Memory and rules files such as CLAUDE.md, AGENTS.md, GEMINI.md and .cursor/rules/ are also looked up in each project's subdirectories, by guard scan and guard scan --ci. The walk goes eight levels deep and reads at most 512 nested files and 16 MiB per project. It skips hidden directories, vendored and build directories such as node_modules, vendor and target, directories the project's .gitignore files exclude, links and a project that is the home directory. Skill, hook and settings files are read at the project root only.

Note: These are heuristics. A security guide that quotes an attack will match, and a paraphrased instruction won't.

Credentials

credentials lists each recognised credential with its type (such as github_token or openai_key), location_class (agent_config, mcp_config, agent_instructions, env_file or package_manager_config), path and lines. The value never appears in the report, in Guard state or in logs.

Each finding has a reference: a keyed hash of the value. Equal references mean the same value, so a key copied from .env into an MCP config shows as one credential in two places. The key stays on the device.

With --credential-attribution, each finding's actor_origin is:

  • agent_tool_call: a Guard hook saw an agent tool call write it.
  • outside_agent_tool_calls: no inspected agent tool call wrote it. This is not proof of a human author.
  • unknown: no evidence either way, including credentials present on the first attributed run.

Packages

Claude Code plugins, Gemini CLI extensions and skills are read file by file as bytes. Nothing is loaded, executed or extracted. Findings cover secret and injection patterns, risky code (credential access, download-and-execute, obfuscation, persistence, native code, process execution, raw network, dynamic eval) and unsafe archives.

Package format 2 also checks declared npm, PyPI, crates.io, NuGet and RubyGems dependency names in supported manifests for resemblance to the catalog's popular-package lists. A package_lookalike finding names the ecosystem, dependency and package it resembles; it does not establish malicious ownership or query a registry. The lists and matching limits are finite, and packages installed later or computed at run time can be missed.

NuGet checks literal IDs in packages.config, .csproj, .fsproj, .vbproj and Directory.Packages.props inside the installed skills, plugins and extensions already scanned. It compares them with a snapshot of the 100 most downloaded NuGet packages over six weeks. It does not scan every repository project, evaluate MSBuild conditions, imports or properties, or resolve restore graphs and transitive dependencies. A conditional literal is a declaration, not proof that it will be installed.

RubyGems checks gem lines in a Gemfile or gems.rb and dependency lines in a .gemspec, read line by line and never run. Only a string literal counts as a name, so names built from variables and Gemfile.lock aren't checked.

Note: Code findings are text heuristics, not behavioral proof. A match says the code mentions a behaviour, not that it runs. No match is not proof of safety.

Output

The report is JSON. The main fields are projects, harnesses, mcp, posture, content, packages, package_findings, credentials, mcp_credentials and, on macOS, screen_recording and privacy_grants. truncated names any limit reached, including an MCP project discovery bound (detailed in mcp.discovery), so missing coverage is never shown as clean.

Coverage

macOS Linux Windows
Credential findings in guard scan Yes Yes Same code; not yet qualified on Windows
guard scan --ci Yes Same code; the CLI tests pass in a Debian container, but it hasn't yet run on a Linux host for this release CLI tests pass, and a run on Windows gave the documented exit codes

CI gate

guard scan --ci runs the same checks over one repository, for pull requests and pipelines. It reads only the repository, uses no Guard home and no network, and executes nothing.

guard scan --ci --repository . --sarif guard.sarif --output guard-scan.json
guard --catalog tenant.json --catalog-key tenant.pub scan --ci --profile production

The signed catalog's scan_gate sets which severity fails the build (critical, high, medium or low). The built-in catalog fails on high and above. A catalog without scan_gate fails on every finding, and findings of unknown severity always fail.

Exit code Result
0 pass: every file read, no blocking finding
1 fail: at least one blocking finding
2 incomplete (something couldn't be read), or a usage or catalog error

--sarif writes SARIF 2.1.0 for code scanning tools. Fetch the catalog and key from a location the pull request can't change.

Assessment

guard assess scores the scan, the sensor's and hooks' findings log and, optionally, an inventory and a package check against a fixed, versioned check table (guard-assessment-score/v3) across seven domains: inventory, harness posture, MCP servers and installed packages, credentials, runtime findings, package versions, and coverage. Each check passes or fails, and each domain and the whole assessment get a number from 0 to 100. The numbers appear only in the local JSON and HTML report, the signed upload and the admin API; the Console shows a band. Checks without evidence are marked not assessed, never passing.

guard assess                                  # JSON to stdout
guard assess --project ~/src/app --format html --output report.html
guard inventory --json > inventory.json && guard assess --inventory inventory.json
guard packages check --json > packages.json && guard assess --packages packages.json
guard assess --baseline last.json             # verify fixes against an earlier report
guard assess --no-runtime                     # leave the findings log out
  • Runtime checks read this device's findings log, the last 7 days by default (--runtime-days, 1 to 30). They are not assessed when there is no log, with --no-runtime, or for a report built from a saved scan without --runtime-findings DIR. A passing runtime check found no matching record, which doesn't prove nothing happened.
  • Package version checks need --packages. A version a malware report names fails, a version an advisory names needs patching and fails once it is past the patch window your organization's package policy sets for its severity, and a runtime or package below a minimum version, or at a version the policy doesn't allow, fails the version policy check. Scoring model v3 added this domain and moved 5 points each to it from harness posture and credentials; Guard Cloud still reads reports scored with v2.

Each finding has a severity band (low, medium, high or critical), an evidence tier, its evidence, a remediation, a suggested owner role and a verification status. With --baseline, findings are new or still_present, and fixed ones are verified_absent or not_verifiable.

Evidence tiers

A finding's tier says how far its evidence goes, and its wording never goes further:

Tier Evidence Wording
exposure A static or heuristic check, such as configuration, a content pattern or a reachable credential "exposed to", "could allow"
runtime One findings log record from an AI agent's process, with its outcome: observed, attempted, prevented or unknown The outcome's own word
occurred A sensitive file or credential access, then within 600 seconds a connection from the same process tree to a destination the egress policy blocks, with bytes sent "data sent"

A hook runs before the step, so a hook record is at most attempted. A chain that would reach occurred without the evidence stays runtime and lists why it was capped. When Guard refused the sensitive file's open, the chain says so: nothing was read. When the access came from an earlier run of the agent that its parent process then started again, the chain is capped and says the send came from a later run. Linux is the only platform qualified for occurred so far; on Windows and macOS every such chain is capped. Guard never writes "compromised", "breached" or "attacked".

--format html writes one self-contained page with no scripts or external requests. Print it to get a PDF.

Privacy

By default a report has no secret values, prompts, file contents or raw paths. Paths appear as unsalted SHA-256 digests, so anyone who can guess a path can confirm it. --local-paths shows full paths for a report that stays on the device; such a report can't be uploaded.

Every report says what it can't see: exposure findings show risk, not exploitation, no tier shows intent, and a passing check is not proof the device is safe.

Upload to Gurd Cloud

Uploading is opt-in, works from an enrolled device, and takes two steps. Nothing leaves the device until you confirm the digest the first step printed.

guard assess --cloud-preview --output preview.json
guard cloud assessment --file preview.json --sha256 <digest>

Guard Cloud keeps the latest report per device and no longer accepts version 1 reports: re-run guard assess. The Console shows a band per device, never numeric scores. A stored report is the device's own account of its configuration, not attestation.

Written from docs/scan.md, docs/assessment.md, docs/detection-catalog.md, docs/vscode-copilot.md, docs/native-model-clients.md at commit fb79778d02f0. Rune Guard is in early access.