Optional ReadonlyadapterThe harness to read the repo as. Defaults to Claude Code, so an existing
Claude Code repo needs nothing. The condition grammar and the block protocol
both come from this adapter's hookProtocol.
Optional ReadonlycapabilitiesThe harness's event capability table — what each event HONOURS — used to
compute HookRunResult.delivery. Defaults to the table of the
registered adapter whose hookProtocol IS protocol (so the default
protocol gets its own harness's table, and no other). A harness with no
table is judged only where the protocol's own injectableEvents proves
delivery, and is unknown elsewhere.
Optional ReadonlycategoriesRestrict the battery to these categories (default: the whole catalog).
Optional ReadonlycwdWorking directory for the process. Default: a value won't be set.
Optional ReadonlyegressAllowlisted egress: let the hook actually reach the network, but ONLY the
listed hosts, with the boundary at the packet layer (an nft allowlist
inside the sandbox netns, fed by slirp4netns) — so a raw socket to an
off-list host is dropped too, which a recordEgress/HTTP_PROXY allowlist
cannot guarantee. Use it to test a hook/skill whose setup needs a real
npm install from a registry you expect, and nothing else. Implies
confinement (it needs the netns), and refuses if bubblewrap + slirp4netns
allowed: true) and HookRunResult.egressDropped (how much off-list
traffic was blocked). See docs/sandboxing.md.Optional ReadonlyenvExtra env vars (merged over process.env). {cwd} in values is left as-is.
Optional ReadonlyeventThe event each disaster is delivered as (default "PreToolUse").
Optional ReadonlyeventsOverride the battery entirely.
Optional ReadonlyrecordRecord the hook's network egress. Implies confinement (the recorder lives in
the sandbox netns, so this forces a sandboxed run and refuses if no sandbox is
available). A recording proxy on loopback captures every host:port a
proxy-honoring tool (npm/pip/curl/fetch) tries to reach — surfaced as
HookRunResult.egress — while the netns still blocks it (nothing
actually leaves). Use it to test what a hook/skill phones home to, or which
registry an install would hit. Raw-socket egress is blocked but not recorded
(it never reaches the proxy) — the block is the boundary, the record is
best-effort observability over it.
Optional ReadonlysandboxConfine the hook under bubblewrap (Linux). When unset, the mode follows
RunHookOptions.trusted: a trusted hook runs directly (false), an
untrusted one is confined-or-refused ("auto"). Set it explicitly to
override: "auto"/"strict" force confinement (a no-egress namespace with a
cleared environment — your opts.env is added back — or a refusal if no
bwrap is available), and false is the opt-out that runs even untrusted code
unconfined. macOS/Windows have no bwrap, so "auto"/"strict" throw there —
see src/sandbox.ts.
Optional ReadonlytimeoutPer-run timeout ms. Default 10000.
Optional ReadonlytrustedProvenance of the hook command. true (default) means YOU authored it — the
usual case at this tier, a command written inline in the test — so it runs
directly. false marks it foreign (a vendored third-party hook script),
which makes confinement the DEFAULT: with no explicit sandbox, an untrusted
hook behaves as sandbox: "auto" — confined under bubblewrap, or refused if
none is available — so foreign code is never run unconfined by accident. This
mirrors the harness tier, where trust follows plugin/pluginDir
provenance (specTrusted in src/sandbox.ts); the unit tier takes a raw
command string with no provenance signal, so you declare it here.
Why this is opt-in (untrusted) and not always-on: the sandbox isn't always
available (Linux + working userns only — forcing it would refuse a hook you
wrote on macOS/hardened CI), it's deliberately hostile (no egress,
--clearenv, empty HOME, read-only fs — a false failure for trusted code that
needs the network or an env var), trust follows provenance (you already
vouched for inline code), and direct exec is ms vs. the confined path's
setup+spawn. See docs/sandboxing.md ("Why confinement is opt-in"). Use
sandbox: "strict" to force confinement even on trusted code.
Options for experimental_verifyPluginGuards.