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)
DecisionStreamExit
allowstdout (suppressed with --quiet)0
warnstderr0
haltstderr2

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.