Skip to main content

Runtime safety for AI agents. Stop agents before they break things.

Project description

CallGuard

Runtime safety for AI agents. Stop agents before they break things.

Nothing sits between an AI agent deciding to call rm -rf / and it happening. CallGuard is that layer. It intercepts every tool call, enforces contracts and operation limits, logs a structured audit trail, and returns actionable error messages so agents self-correct instead of failing silently. Zero runtime dependencies. Drop it in front of any tool-calling agent.

Why CallGuard

  • Audit trail for every tool call. Structured JSON events with automatic redaction of secrets (OpenAI keys, AWS credentials, JWTs, GitHub tokens). Know exactly what your agent did, when, and why it was allowed or denied.
  • Agents self-correct from actionable denials. When CallGuard blocks a tool call, it tells the agent why with a specific, instructive message. The agent adjusts its approach instead of retrying blindly.
  • Observe mode for shadow deployment. Run the full governance pipeline without blocking anything. Audit events log CALL_WOULD_DENY so you can tune rules before enforcing them in production.
  • Zero runtime dependencies. Pure Python 3.11+. OpenTelemetry support via optional callguard[otel].

Install

pip install callguard

Optional OpenTelemetry support:

pip install callguard[otel]

Requires Python 3.11+.

Quickstart

import asyncio
from callguard import CallGuard, CallGuardDenied, deny_sensitive_reads

guard = CallGuard(contracts=[deny_sensitive_reads()])

async def read_file(file_path):
    return open(file_path).read()

async def main():
    # This succeeds
    result = await guard.run("Read", {"file_path": "/tmp/notes.txt"}, read_file)

    # This raises CallGuardDenied
    try:
        await guard.run("Read", {"file_path": "/home/user/.ssh/id_rsa"}, read_file)
    except CallGuardDenied as e:
        print(e.reason)
        # "Access to sensitive path blocked: /home/user/.ssh/id_rsa.
        #  This file may contain secrets or credentials."

asyncio.run(main())

See docs/quickstart.md for Claude Agent SDK integration, custom contracts, hooks, and audit configuration.

Key Concepts

Every tool call is wrapped in a ToolEnvelope -- a frozen, deep-copied snapshot of the invocation (tool name, args, side-effect classification, environment). Envelopes are immutable. Nothing downstream can tamper with the original args.

Contracts define governance rules. A @precondition runs before execution and can deny the call. A @postcondition runs after and emits warnings (observe-only in v0.0.1 -- it never blocks). A @session_contract checks cross-turn state like total execution counts. All return a Verdict: either Verdict.pass_() or Verdict.fail("actionable message").

Hooks are lower-level interception points. A before-hook receives the envelope and returns HookDecision.allow() or HookDecision.deny("reason"). After-hooks observe the result. Hooks run before contracts in the pipeline.

The GovernancePipeline evaluates five steps in order: attempt limit, before-hooks, preconditions, session contracts, execution limits. First denial wins. If everything passes, the tool executes, then postconditions and after-hooks run.

CallGuard tracks two counter types. max_attempts caps all governance evaluations, including denied ones -- this catches denial loops where an agent keeps retrying the same blocked call. max_tool_calls caps only successful executions. Both fire independently.

In observe mode, the full pipeline runs and audit events are emitted, but denials are converted to CALL_WOULD_DENY and the tool executes anyway. Use this for shadow deployment: see what would break before you enforce it.

Audit and redaction happen at write time. Every tool call emits a structured AuditEvent to a configurable sink (stdout, file, or custom). RedactionPolicy strips sensitive keys, detects secret value patterns, redacts bash credentials, and caps payloads at 32KB. Redaction is destructive by design -- no recovery path.

What This Is NOT

  • Not prompt injection defense.
  • Not content safety filtering.
  • Not network egress control.
  • Not a security boundary for Bash. (BashClassifier is a heuristic, not a sandbox.)
  • Not concurrency-safe across workers. (MemoryBackend is single-process.)

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

callguard-0.1.0.tar.gz (30.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

callguard-0.1.0-py3-none-any.whl (21.0 kB view details)

Uploaded Python 3

File details

Details for the file callguard-0.1.0.tar.gz.

File metadata

  • Download URL: callguard-0.1.0.tar.gz
  • Upload date:
  • Size: 30.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for callguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 e1d34395b258f4d29b1807b863235f29632012f8a3767dff5439337bcf743480
MD5 3c4010caec6b5e477b3ab89e61e34e6b
BLAKE2b-256 386c5b34d7d30c8f00ef52d3c1b007e8bb58129eae1dfd3dda2ac40812cd4d25

See more details on using hashes here.

File details

Details for the file callguard-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: callguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 21.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for callguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 916feab4cddee9db1937c20d22f26db6462a189a95a1c86fca755afd85d596a2
MD5 915eb21f85e33b2c8ade05d3ea6f8584
BLAKE2b-256 d881c8e8f4db19a4964ce2b06d853b7ae9da083ce07946e02dfa9110aa17e3be

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page