vigiles API reference
    Preparing search index...

    Interface VerifyGuardrailOptions

    Options for runHook — every RunScriptOptions knob except stdin, which the hook layer owns (it serializes the event there).

    interface VerifyGuardrailOptions {
        capabilities?: EventCapabilityTable;
        categories?: readonly DisasterCategory[];
        condition?: string;
        cwd?: string;
        egress?: { allow: readonly string[] };
        env?: Record<string, string>;
        event?: string;
        events?: readonly DisasterEvent[];
        protocol?: HookProtocol;
        recordEgress?: boolean;
        sandbox?: SandboxMode;
        timeoutMs?: number;
        trusted?: boolean;
    }

    Hierarchy (View Summary)

    Index

    Properties

    capabilities?: EventCapabilityTable

    The 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.

    categories?: readonly DisasterCategory[]

    Restrict the battery to these categories (default: the whole catalog).

    condition?: string

    The hook's CONDITION as the harness config writes it — Claude Code's if, e.g. "Bash(git push *--force*)". When the condition does not match the event, the harness never spawns the hook, so neither do we: the result comes back ran: false, blocked: false.

    🔴 PASS IT WHENEVER THE HOOK YOU ARE TESTING DECLARES ONE. A conditional hook tested without its condition is tested as an unconditional one, and since the bodies of real conditional guards are unconditional denies, that reports the guard as blocking everything you feed it. See core/hook-condition.ts.

    cwd?: string

    Working directory for the process. Default: a value won't be set.

    egress?: { allow: readonly string[] }

    Allowlisted 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

    • nft aren't available. The hosts are resolved to IPs at launch; results land in HookRunResult.egress (the allowlisted hosts that were reached, with allowed: true) and HookRunResult.egressDropped (how much off-list traffic was blocked). See docs/sandboxing.md.
    env?: Record<string, string>

    Extra env vars (merged over process.env). {cwd} in values is left as-is.

    event?: string

    The PreToolUse event name to wrap each disaster in (default "PreToolUse").

    events?: readonly DisasterEvent[]

    Override the catalog entirely (e.g. a project-specific battery).

    protocol?: HookProtocol

    The harness whose wire protocol + condition grammar to use. Defaults to Claude Code, so every existing caller is unaffected.

    recordEgress?: boolean

    Record 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.

    sandbox?: SandboxMode

    Confine 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.

    timeoutMs?: number

    Per-run timeout ms. Default 10000.

    trusted?: boolean

    Provenance 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.