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.
Install and remove
Section titled “Install and remove”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.
# 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-codeStart 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:
ov hook install claude-code --scope project --dry-runov hook install claude-code --scope projectov hook uninstall claude-code --scope projectUser/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.
Threshold and denial reasons
Section titled “Threshold and denial reasons”--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.
Boundaries
Section titled “Boundaries”- Only text in the proposed tool input is scanned. Existing
old_stringcontent 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
updatedInputafter 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.