Skip to content

ov hook

ov hook checks the text Claude Code is about to write through Write, Edit, MultiEdit, or NotebookEdit. A finding at or above the selected severity blocks that tool call before the write and tells the agent to store the value with vault_create_secret and use vault_run or ov run at runtime.

This is a guard against accidents by a cooperative agent, not a security boundary. Detection is local and needs no account. The hook runtime does not read the destination file, contact a server or vault, or run the CLI update check. A clean result emits no permission decision, preserving Claude Code’s normal permission flow.

Use an installed Unix ov binary, not go run or a temporary build. Claude Code 2.1.282 is the tested version; earlier versions are not qualified. The tested version has Write, Edit, and NotebookEdit; MultiEdit is included for compatibility but was not available in that version.

Terminal window
# Preview OpaqueVault's proposed entry without printing unrelated settings.
ov hook install claude-code --dry-run
# Install for your user (default scope).
ov hook install claude-code
# More sensitive: also block medium-severity findings.
ov hook install claude-code --block-at medium
# Remove only OpaqueVault's hook entry.
ov hook uninstall claude-code

Start a new Claude Code session after changing installation. The tested headless CLI retained its hook snapshot during a session; interactive /hooks review behavior was not qualified.

Scope Command flag Settings target
User (default) --scope user ~/.claude/settings.json
Shared project --scope project .claude/settings.json in the current directory
Local project --scope local .claude/settings.local.json in the current directory

Run project/local installation from the directory where you launch Claude Code. The installer does not climb to a Git root: the tested CLI ignored parent Git-root settings when launched from a nested directory. Use the same scope to uninstall:

Terminal window
ov hook install claude-code --scope project --dry-run
ov hook install claude-code --scope project
ov hook uninstall claude-code --scope project

User/local installation uses the ov path on PATH when it identifies the running binary, which helps preserve Homebrew/Linuxbrew upgrade paths. Otherwise it uses the executable path. If another ov is first on PATH, installation warns: the fallback can be version-specific and break after an upgrade. The selected user/local command must have the basename ov so reinstall and uninstall can recognize it. If the fallback executable has another name, installation refuses before changing settings; run a binary named ov or put an ov symlink to the same binary on PATH. Project installation writes bare ov so teammates do not inherit a personal path. Each teammate needs an appropriate installed binary on Claude Code’s PATH, which may differ from an interactive shell or a GUI launch. An untrusted PATH can substitute another binary.

Installation refuses root, Windows, temporary executable paths, invalid settings, and ambiguous settings objects. User settings symlinks are resolved and written through; project/local settings symlinks are refused. Existing permissions and unrelated settings values/order are preserved; whitespace is normalized to two spaces. Reinstalling is idempotent. Writes use an atomic replacement, but a concurrent settings edit between reading and replacement can be lost. Dry-run prints OpaqueVault’s proposed change without dumping other hooks or settings.

--block-at high is the default. Accepted values are exactly critical, high, and medium. critical permits high findings through; it does not disable the scanner. The runtime has a five-second internal deadline and the installed Claude hook has a ten-second external timeout.

Credential denials are built from detector names, severities, and fixed guidance; their schema excludes values, source snippets, file paths, and line numbers. Incomplete checks receive a fixed reason instead:

Reason Meaning
timeout The internal deadline expired.
too_large The bounded input decoder could not complete within its input limit.
parse_error The expected event/input envelope could not be decoded.
invalid_arguments Runtime arguments or threshold were not recognized.
incomplete Scanner limits or another coverage constraint prevented a complete scan.
scan_failed Scanning failed or returned an unrecognized result.

For an unexpected denial, inspect the installation using ov hook install claude-code --dry-run with the installed scope. A qualifying credential finding takes precedence over an incomplete report. Input annotations such as ov:allow, ovscan:test-fixture, and example comments do not bypass the hook. Scanner limits include 10 MiB per string, 100 MiB total, 10,000 entries, and 1,000 findings; reaching a limit can deny an otherwise legitimate write.

For integrations, the installed command is ov hook claude-code pre-tool-use --block-at high. It consumes one stdin JSON envelope and emits either nothing or one PreToolUse deny JSON line. A clean result has empty stdout rather than an allow decision. Normal runtime exit status is zero. This path runs before normal command dispatch and is intentionally not a Cobra subcommand.

  • Only text in the proposed tool input is scanned. Existing old_string content being removed and top-level destination paths are excluded. The destination file and composed post-edit file are not read. Unknown string fields are scanned.
  • Bash writes (echo >, sed -i, cp) and MCP editing tools, including Serena, are outside the installed matcher.
  • Credentials split across multiple calls or edits can escape detection. Encodings other than detectable whole-value base64, including hex, reversal, and zero-width insertion, are outside this guard’s promise.
  • Another hook can replace input with updatedInput after this hook scans it. On the tested CLI, our deny wins against a parallel allow; our clean result does not certify the replacement text.
  • Agents or users can change settings, set disableAllHooks, or uninstall through Bash. Project settings can disable user hooks. This is not tamper resistance.
  • A missing executable, a binary older than hook support, a startup/core-dump setup failure, Claude Code’s external timeout, or signal death can let Claude Code proceed. Windows has no runtime support; a hand-wired command fails open. Do not treat all failure paths as fail-closed.
  • Randomized matching tests exercise detector coverage and value-free output; they do not establish universal detection of credential formats.