The runtime gate
skillsvault gate is the enforcement point. It evaluates a single skill invocation against the locally-synced, signed bundle and returns allow, warn, or halt — entirely in-process, with no network call and no model on the path. That is what keeps enforcement instant and working offline.
The decision is local
The gate reads ~/.skillsvault/bundle.json (written by skillsvault sync) and decides from it alone. Shipping the audit event afterwards is best-effort and off the decision path — the decision never waits on the network.
Evaluation
Given a skill name, the gate checks it against your organisation's approved catalogue and every policy that applies, and returns a single decision:
- allow — the skill is approved for use at this version.
- warn — usable, but flagged (for example, deprecated); the reason is printed.
- halt — do not use it; the reason names the policy and, where one is set, the approved replacement.
When several rules apply to the same skill, the most restrictive outcome wins — a ban is never downgraded by a broader rule. Skills that aren't in the catalogue but live in a managed namespace fall back to your organisation's non-compliance default, so sideloaded copies don't slip through.
Decisions are deterministic: the same skill and the same policy set always produce the same verdict, on every machine. The console's policy builder previews "this matches N skills" using the same rules, so what you see when authoring a policy is exactly what the gate enforces. See Policies for how rules are written.
Two modes
gate behaves differently depending on whether a human ran it or a harness hook piped a payload to it.
Human / CI mode
Pass --skill. The decision is rendered and the exit code is the protocol:
skillsvault gate --skill acme/hubspot-crm-entry
# ✓ ALLOW acme/hubspot-crm-entry@1.3.0 (exit 0)
skillsvault gate --skill acme/onboard-customer
# ⚠ WARN acme/onboard-customer — Skill is deprecated. (exit 0, stderr)
skillsvault gate --skill acme/teamleader-crm-entry
# ⛔ HALT acme/teamleader-crm-entry
# policy: Teamleader retired
# Use acme/hubspot-crm-entry instead. (exit 2)
| Decision | Stream | Exit |
|---|---|---|
| allow | stdout (suppressed with --quiet) | 0 |
| warn | stderr | 0 |
| halt | stderr | 2 |
A non-zero exit is what a generic hook treats as a veto. --quiet suppresses the allow line so a passing gate is silent.
Claude Code hook mode
When no --skill is given, the gate reads the harness's PreToolUse payload from stdin and emits a structured permission decision instead of relying on the exit code:
echo '{"tool_name":"Skill","tool_input":{"skill":"acme/teamleader-crm-entry"}}' | skillsvault gate
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "skillsvault blocked this skill — policy: Teamleader retired. Use acme/hubspot-crm-entry instead."
}
}
In hook mode the deny is the veto, not the exit code
Hook mode always exits 0. A halt is signalled by permissionDecision: "deny" with the org's reason (surfaced to the agent); a warn prints to stderr; an allow emits nothing. This is deliberate — Claude Code reads the JSON, not the exit status, for PreToolUse. Wire it with skillsvault install-hook.
Skill extraction from the payload
In hook mode the gate recognises the skill name in the hook payload automatically — you don't need to shape tool_input for it. If a payload contains no recognisable skill name, the gate does not block; it stays out of the way.
The byte-clean contract
The gate's output is a protocol, so presentation never corrupts it: ANSI color is emitted only to a real terminal and is suppressed by --no-color, NO_COLOR, or TERM=dumb. Styling never alters a decision, an exit code, the stdout-vs-stderr split, or the hook JSON. Piped and hook output is always plain bytes.
Audit
After deciding, the gate ships one audit event to POST /api/audit/ingest with the skill, resolved version, harness, machine, decision (allowed / warned / blocked), reason, and matched policy. It is fire-and-forget: a failure to ship never changes or delays the decision. See Audit log.