DocumentationReference

HazelJS Agent Gatekeeper Package

npm downloads

HazelJS Agent Gatekeeper is the runtime authorization and policy-enforcement layer that controls every tool action attempted by an agent.

Every tool call authorized before execution.

Quick Reference

  • Purpose: Deterministic per-invocation authorization before protected tool execution.
  • When to use: Any agent tool that mutates data, spends money, crosses tenants, or is destructive.
  • Key APIs: AgentGatekeeper, evaluate, execute, simulate, fromFunction, fromHazelTool, fromSkillgate, protectMcpInvoke, createToolExecutorGate, createAuditTransportSink, createOtelAuditSink, createRedisApprovalProvider.
  • Dependencies: zod, yaml. Optional peers: @hazeljs/​agent, @hazeljs/​skillgate, @hazeljs/​audit, @opentelemetry/​api.
  • Common mistakes: Trusting input.tenantId; mode: 'audit' in production; wrapping MCP without protectMcpInvoke; InMemoryAuditSink / InMemoryApprovalProvider in a multi-replica deployment.

Installation

npm install @hazeljs/​agent-gatekeeper

Minimal example

import { AgentGatekeeper, fromFunction, ConsoleAuditSink } from '@hazeljs/​agent-gatekeeper';

const gatekeeper = new AgentGatekeeper({
  mode: 'enforce',
  defaultDecision: 'deny',
  policies: [
    {
      id: 'refund-agent-stripe-policy',
      version: '1.0.0',
      match: { agents: ['refund-agent'], tools: ['stripe.refund'] },
      rules: {
        allowWhen: ({ input, context }) =>
          input.amount <= 100 && input.tenantId === context.tenantId,
        requireApprovalWhen: ({ input }) => input.amount > 50,
      },
    },
  ],
  auditSink: new ConsoleAuditSink(),
});

await gatekeeper.execute({ context, tool: fromFunction('stripe.refund', refund) });

ConsoleAuditSink writes JSON to stdout (ship with your log collector). InMemoryAuditSink is tests-only.

Production (horizontal scale)

Authorization is stateless: the same policies + trusted ToolInvocationContext produce the same decision on every replica. Audit and approvals must be shared.

import { KafkaAuditTransport } from '@hazeljs/​audit';
import {
  AgentGatekeeper,
  CompositeAuditSink,
  ConsoleAuditSink,
  createAuditTransportSink,
  createOtelAuditSink,
  createRedisApprovalProvider,
} from '@hazeljs/​agent-gatekeeper';
import { trace } from '@opentelemetry/​api';

const gatekeeper = new AgentGatekeeper({
  mode: 'enforce',
  defaultDecision: 'deny',
  policies: [refundPolicy],
  auditSink: new CompositeAuditSink([
    new ConsoleAuditSink(),
    createAuditTransportSink(
      new KafkaAuditTransport({
        sender: kafkaProducer,
        topic: 'hazel.gatekeeper.audit',
        key: (event) => String(event.resourceId ?? event.actor?.id ?? ''),
      })
    ),
    createOtelAuditSink({ trace }),
  ]),
  approvalProvider: createRedisApprovalProvider(redis),
});
Audit sinkScaleNotes
InMemoryAuditSinkNoTests only. Lost on restart, split per replica.
ConsoleAuditSinkYes, via log shipperDefault. JSON stdout → collector.
createAuditTransportSink(KafkaAuditTransport)YesShared topic. Awaited; fail-closed in enforce.
createOtelAuditSinkYesSpans to the collector.
Approval providerScale
InMemoryApprovalProviderNo — tests / single process
createRedisApprovalProvider(redis)Yes — create / resolve / consume on any replica
createApprovalStoreProvider(RedisApprovalStore)Yes — via @hazeljs/​agent
createHumanTaskProvider(sqlHumanTasks)Yes for get/resolve; prefer Redis for atomic consume

Resume after HITL on another replica:

await replicaB.resolve(approvalId, 'approved', 'operator-1');

await replicaA.execute({
  context: { ...context, approvalToken: approvalId },
  tool,
});

CLI

hazel gatekeeper validate --config agent-gatekeeper.yaml
hazel gatekeeper simulate --agent refund-agent --tool stripe.refund --input input.json
hazel gatekeeper explain invocation.json

Never executes tools.

Guide

Architecture, policy authoring, approvals, adapters, and rollout: Agent Gatekeeper guide.