Interactive Walkthrough · April 2026

What happens when an agent tries to publish source code?

Walk through a real scenario. See the gate's input, policy evaluation, and decision at every step. No AI in the enforcement path.

1
Agent issues tool call

An AI agent running in Claude Code wants to publish a package. It issues a Bash tool call. The agent does not know it is governed.

{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm publish @company/internal-code --access public"
  },
  "session_id": "session-a3f9c2"
}

This JSON is sent to the gate on stdin before the command executes.

2
Gate loads and verifies signed policy

The gate reads the policy file and verifies its Ed25519 signature against the public key. If the signature is invalid — if anyone has modified the policy without re-signing — the gate denies all actions. The agent does not possess the signing key.

// Policy signature (simplified)
{
  "version": "2.6.0",
  "default_action": "deny",
  "signature": {
    "algorithm": "ed25519",
    "value": "mOiZF8E3MKF..."   // signed by human
  }
}

bin/zlar-gate lines 651-660 — signature verification

3
Rule matches: npm publish → ask human
ASK

The gate pattern-matches the command against its rules. Rule R-PUBLISH matches npm publish with risk score 95/100. The action is ask — route to a human for approval.

{
  "id": "R-PUBLISH",
  "domain": "bash",
  "pattern": "\\bnpm\\s+publish",
  "action": "ask",
  "risk_score": 95,
  "severity": "critical"
}

Pattern matching is regex/prefix/contains/exact — no reasoning, no inference.

4
Gate denies immediately, notifies human
DENY

The gate does not wait for the human before responding. It denies the action immediately and tells the agent to retry after the human decides. The command never executes while pending.

// Gate response to Claude Code (stdout)
{
  "hookSpecificOutput": {
    "permissionDecision": "deny",
    "permissionDecisionReason":
      "Awaiting Telegram approval (rule R-PUBLISH). Approve on phone, then retry."
  }
}

Simultaneously, the gate sends a Telegram message to the human with the exact command, risk score, and approve/deny buttons.

bin/zlar-gate line 1468 — deny while pending

5
Human sees: "npm publish @company/internal-code"

The human's phone buzzes. Telegram shows the exact command, the rule that triggered, and the risk score. Two buttons: Approve or Deny.

ZLAR Gate

Tool:    Bash
Action:  npm publish @company/internal-code --access public
Risk:    95/100
Rule:    R-PUBLISH

[Approve]  [Deny]

The human sees what is about to be published. With this context, the human taps Deny. The 512,000 lines never reach the public registry.

If the human does not respond, the gate times out and denies. If Telegram is unreachable, the gate denies. There is no path where the action proceeds without explicit human authorization.

6
Audit trail records the decision

Two hash-chained, Ed25519-signed audit entries are written. The request and the resolution. Tamper with any entry and every subsequent hash breaks.

// Entry 1: The request
{
  "outcome": "pending",
  "action": "npm publish @company/internal-code --access public",
  "rule": "R-PUBLISH",
  "risk_score": 95,
  "authorizer": "gate",
  "prev_hash": "a1b2c3...",
  "signature_algorithm": "Ed25519"
}

// Entry 2: The human decision
{
  "outcome": "denied",
  "authorizer": "human:7662799203",
  "prev_hash": "d4e5f6...",   // SHA-256 of entry 1
  "signature": "Kp7xR2vN..."
}

authorizer: "human:7662799203" — a specific human, identified by Telegram ID. Non-repudiable.

Every path leads to the same conclusion

In every scenario, either a human authorized the action (by signing the policy or by explicitly approving), or the action was blocked. There is no state in which an unauthorized action executes.

The gate has no intelligence. It cannot be prompted, persuaded, or reasoned with. The absence of intelligence is the security property.

Read the formal proof  ·  Run the simulation yourself  ·  View the source