Skip to documentation

Plugin 0.3.3 · Operator reference

AgentGuard for Claude Code,
Codex and ChatGPT Work

Signed tool decisions, matter budgets and session tool policies. Full Enforce is free for one machine, no account or key needed. Add a key for Solo or Team.

Codex and ChatGPT Work

codex plugin marketplace add MerchantGuard/agentguard-codex-plugin
codex plugin add agentguard@agentguard
npm ci

Run the first two commands in your terminal. Change into the installed plugin root reported by Codex before running npm ci. Then start a session and review all five commands in /hooks.

Requires Node.js 22. The public marketplace selects the Codex 0.154 compatibility installation. Installing a plugin does not trust its hooks.

Claude Code

claude plugin marketplace add MerchantGuard/agentguard-codex-plugin
claude plugin install agentguard@agentguard
npm ci

Run npm ci in the installed plugin root reported by Claude Code. The plugin uses the same local policy engine, signed records and existing licenses across hosts.

Complete the normal marketplace installation and workspace trust prompts in Claude Code. Its /hooks menu is read-only: inspect the configured plugin hooks there. It does not perform the Codex hash-trust action. See Claude Code's workspace trust reference.

Details

This page follows the public plugin source. It describes the runtime's current behavior, including what happens when a hook cannot answer.

Coverage and limits

AgentGuard applies policies to the local tool calls that Claude Code, Codex or ChatGPT Work sends through its supported hook path, including plugin MCP tools. Codex uses two PreToolUse gates and one PostToolUse recorder matching .*. A separate SessionStart hook starts license resolution, and SessionEnd stops renewal. The Claude Code integration uses the same policy engine.

Burn gate

Uses the published Burn gateway for subagent spawning and sustained-burn observations. It keeps the existing Burn policy, reservations, ledger and receipts under AGENTGUARD_HOME or ~/.agentguard.

Spend gate

Applies tool patterns, capability tiers and configured unit costs to other tool calls. Supported hook paths include Bash, apply_patch, Edit, Write, update_plan, spawn_agent and MCP tools.

Claude Code WebSearch and WebFetch are hookable tools. In Codex and ChatGPT Work, hosted tools such as WebSearch are outside this hook path. Web ChatGPT is unsupported. In Codex, an already-approved shell session can receive write_stdin without another pre-tool decision, and specialized tool paths can opt out. This is not universal interception. See the Codex tool coverage documentation.

Hooks are fail-open

An internal error allows the call, exits successfully, emits a one-line warning and records a fail-open event when local storage is writable. A dead process, full disk, inaccessible directory or disabled hook can prevent that record. If the signed writer is unavailable but storage works, the client queues content-free recovery metadata. Those pending rows are unsigned until recovery. An absent receipt does not prove that a tool was never used.

The warm response budget defaults to 250 ms. hookBudgetMs can change it up to 1900 ms, leaving 100 ms before the host's two second timeout. Cold startup keeps its 1500 ms budget. Process startup, scheduling and operating-system delays can add wall time. Burn's combined synchronous decision, reservation, receipt and ledger operation remains inside the same response budget.

Keep authorization at the tool or service boundary when a failed hook must never permit access. MDM delivery does not turn fail-open hooks into fail-closed authorization.

Install, provision and trust

  1. Choose the three installation commands for your host above. Use Node.js 22 on macOS or Linux. Windows execution is not verified.
  2. Keep npm lifecycle scripts enabled. The locked dependencies are published @agentguard-run/spend ^0.20.0 and @agentguard-run/burn ^0.2.3, with no sibling links. Codex marketplace installation does not install them automatically.
  3. In Codex, start a new session, open /hooks, inspect the startup command, both tool gates, the receipt command and the session-end command, then trust the reviewed definitions.
  4. Use agentguard-status to check the effective mode. Without a key, the local policy controls the mode and defaults to enforce. A failed paid license selects shadow with its reason.

Provisioning may access npm. Hooks communicate through a private filesystem mailbox and never open a socket. In a Codex cache installation, postinstall also provisions the locked dependencies in the persistent plugin data directory, because Codex 0.154 can replace its install cache at session start. Run npm ci again after a lockfile change. For a managed installation, set PLUGIN_DATA to the intended private runtime directory during provisioning.

Claude Code installs locked Node dependencies when it caches a Git marketplace plugin, with lifecycle scripts disabled. A local-directory marketplace does not install dependencies automatically. Run the explicit npm ci provisioning step with lifecycle scripts enabled so the plugin can prepare its runtime. See the Claude Code dependency reference.

In Codex, trust is pinned to each normalized hook definition. Changed definitions need review again. User hooks can take precedence over conflicting plugin decisions, and other matching hooks can run alongside these hooks. Read the host's review and trust guide.

Codex 0.154 compatibility

The marketplace points at compat/codex-0.154/agentguard. This generated installation has .codex-plugin/plugin.json and .mcp.json, with the same hook code, policies, skills and assets. Codex 0.154 skips portable hook sources, so installing the portable root directly can expose MCP tools without running the hooks.

Compatibility hooks return an empty success response for unchanged allowed input, as required by that release's parser. Denials and signed decisions retain their values. The MCP launcher derives the same data directory as the hooks from the validated installation cache path, or accepts an explicit PLUGIN_DATA. Future host versions and ChatGPT Work need their own installation validation.

Maintainers regenerate compatibility files with npm run build:compat and check them with npm run check:compat. See the source-linked compatibility reference.

Private workspace marketplace

A firm can distribute its reviewed copy through .agents/plugins/marketplace.json, pointing the plugin entry at the compatibility installation. Register that marketplace and install from it. A marketplace named firm uses the selector agentguard@firm. In a supported desktop surface, select the marketplace in the Plugins Directory and install in a new chat. Node, scripts and dependencies must exist wherever Work executes them.

A public repository is not a universal directory submission. Workspace-wide publication is a separate administrator action. See the host's marketplace and packaging guide.

Free and paid modes

The same free and paid split applies in Claude Code, Codex and ChatGPT Work. There is no separate Claude Code tier.

Existing license tiers, with no separate plugin plan
LicensePlugin behaviorSeats
FreeOne machine, no account, full Enforce, local signed receipts and Burn. No license key needed.One machine
SoloA license key for up to three machines, the dashboard, receipts export and email support.3
TeamOne org policy every seat runs, seats you add and revoke, one invoice.10
Existing 50-seat licenseThe same paid plugin features.50

Team is $19.90 per seat per month or $199 per seat per year, starting at three seats. Choose 3 to 100 seats. Ten seats cost $199 per month or $1,990 per year.

Add a key for Solo or Team. Activation is optional for Free. Use an existing license or see pricing. Ask the agentguard-policy skill to activate license <KEY>. Its helper accepts the key on standard input, writes licenseKey into ${PLUGIN_DATA}/policy.json, preserves unrelated settings and resolves the current session again. Do not put the key in shell command arguments or an audit entry. AGENTGUARD_LICENSE_KEY takes precedence over a saved key.

With a configured key, at session start a detached process makes one license refresh attempt through the Spend SDK. The two second deadline covers validation and seat registration. Hook processes only read the local result. Without a usable cached license, they stay in shadow while resolution finishes.

A previously valid cache can remain usable offline for seven days after expiresAt. An explicit server rejection does not receive that grace. Failed license, seat or org refreshes select shadow even during grace. After the grace period, the engine selects shadow with license_required. An exceeded seat limit selects shadow with seat_limit. Licensing never denies a tool call.

The shared KV store counts up to three active machines for Solo, and active sessions across machines for Team. Solo sessions on the same machine share its allowance; a fourth machine selects shadow with seat_limit. A seat stays active for fifteen minutes after its last heartbeat, and its license record expires after twenty four hours. Each live session renews every five minutes from the worker, never from a hook. Heartbeat failures and later over-limit responses select shadow with a reason. A revoked registration selects seat_revoked until a successful heartbeat explicitly restores it. Status shows seats used, the limit, seatStorage and seatsVerified. Memory fallback is explicitly unverified. A failed heartbeat marks any retained count as an older, unverified observation. Renewal stops at SessionEnd or host exit; an unidentified host uses a fifteen minute lease renewed by tool activity.

Policy file reference

The runtime reads ${PLUGIN_DATA}/policy.json. If it is absent, the packaged default requests enforce mode, permits capability tiers through payment_execute, and sets no user-defined unit prices, caps, explicit denies or session mappings. The built-in guard-pack still checks dangerous command patterns. Licensing still controls the effective mode. A corrupt file causes a recorded fail-open.

A paid operator can set teamPolicyFile or the AGENTGUARD_PLUGIN_POLICY environment variable. Relative paths resolve from PLUGIN_DATA; the environment variable takes precedence. Shared top-level fields replace matching local fields, rather than merging nested rules. A local license key is preserved. Free sessions use the personal rules. Burn keeps its separate existing policy.

AgentGuard's server is a control plane for policy, not data. It stores the policy your admin writes, which seats are licensed, and which policy version each seat last reported. It never receives a tool call, a prompt, a file, or a receipt.

Team and 50-seat accounts can publish an org policy in the dashboard. The detached worker fetches it at session start and every fifth heartbeat, validates its canonical SHA256, and keeps the last good copy in ${PLUGIN_DATA}/org-policy.json. Hooks read local files only. A failed refresh keeps that copy and selects shadow with a reason; it never denies a tool call.

The org policy is the root above the shared team file and local policy. Tool allowlists intersect by matching semantics, deny patterns and ethical walls combine, the lowest capability ceiling wins, and caps append. Org enforce mode cannot be lowered locally. Root and session constraints both apply. Matching org tool-rule fields and org actor identifiers take precedence. The old shared-file replacement behavior remains when the org has never published.

Published org policies accept only the fields in this reference, except licenseKey and teamPolicyFile, which are local-only. Unknown fields are rejected at every level. Identifiers allow letters, digits, underscores, dots, slashes, colons, at signs and hyphens, up to 128 characters. Expressions have a 512-character limit; each list and session map has at most 256 entries. The policy's version is always 1; the published envelope has a separate increasing version.

Heartbeats carry exactly the license key, machine fingerprint, process id and loaded org policy hash. Seat activity shows the last heartbeat and whether its hash matches the published policy. A matching hash does not attest enforcement. They contain no tool input, output, prompt or file content. Admin labels and invite emails stay on the server. Revoked seats select shadow at the next heartbeat, and can be restored in the dashboard.

Use identifiers and regular expressions, not document content, prompts or provider credentials. licenseKey is the sole credential exception and never enters the ledger. Protect policy files from agent modification when they serve as organizational controls.

Root fields

All root fields consumed by plugin 0.3.3
FieldValue and behavior
versionRequired integer 1. Other values are invalid.
tenantIdTenant identifier used in actor scope and cap keys. Default: local.
modeenforce or shadow; omitted means enforce is requested. Free enforces the local policy on one machine without a key.
hookBudgetMsPositive safe integer for the warm response budget. Default: 250. Values above 1900 are capped; invalid values use the default. Cold startup stays at 1500.
licenseKeyLicense key stored locally by the activation helper. AGENTGUARD_LICENSE_KEY overrides it. Never copy it into a shared policy or receipt.
teamPolicyFilePaid shared policy path. A relative path starts at PLUGIN_DATA. AGENTGUARD_PLUGIN_POLICY overrides this selection.
defaultMatterIdDefault identifier for actor.taskId. A session's matterId overrides it.
maxCapabilityHighest permitted capability tier. Omission adds no ceiling; the packaged default is payment_execute.
allowedToolsArray of tool-name regular expressions. Omission allows tools past this check; an empty array matches nothing. Global and session allowlists both apply.
deniedToolsArray of explicit tool-name deny patterns. Default: empty. Independent of costs and allowlists.
ethicalWallArray of tool-name deny patterns recorded as ethical_wall. Default: empty. This does not inspect document content or infer conflicts.
paymentPatternCase-insensitive expression tested against the provider and tool/model name. Default: payment|pay_|charge|transfer|checkout|purchase. A match claims at least payment_initiate.
toolRulesOrdered array of matching tool rules below. Later matching rules replace values they explicitly supply.
guardPackRoot object with a rules map from the built-in IDs below to stop, warn or off. Unknown IDs and fields are rejected. Only a shared team or org policy can downgrade a rule.
capsArray of spend caps below. Matching global and session caps apply together, with the most restrictive exceeded action winning.
sessionsObject keyed by actual host session ID. Each value accepts the session fields below. Unknown session IDs use the root rules.

Tool patterns use case-insensitive JavaScript regular expressions. Anchor an exact tool name with ^ and $. Tool rule patterns and list patterns have a 512-character limit. A matching deny or wall remains a restriction even when a monetary cap would allow the call. In shadow mode these checks record what would happen without denying the tool.

Built-in guard-pack

Every install carries these deterministic checks. The hook inspects tool names and arguments locally in memory, then passes only matched rule IDs to the signing worker. It does not call a model or send arguments to a server. Each rule defaults to stop in enforce mode on every tier. Shadow sessions record WARN without denying the call.

A shared team policy can list rule IDs with warn or off. An org policy is the root: an omitted rule keeps its default STOP, and lower layers cannot weaken it. A lower layer can tighten an explicitly downgraded rule. A personal policy alone cannot disable the pack. Scan limits, unreadable branch state and internal failures select shadow with a reason. Protect these policy files from agent writes.

{"version":1,"guardPack":{"rules":{"GP001":"warn","GP014":"off"}}}
Guard-pack rule IDs and reasons
IDReasonDefault in enforce
GP001A downloaded response is being executed by a shell or Python.STOP
GP002Recursive deletion targets a root, home, current directory or shallow wildcard.STOP
GP003A force push targets the shared main or master branch.STOP
GP004A hard reset targets a shared branch or a branch whose state is unknown.STOP
GP005Git clean would remove untracked directories and ignored files.STOP
GP006Terraform would apply infrastructure changes without interactive approval.STOP
GP007Kubernetes would delete a namespace.STOP
GP008A cloud command would delete, terminate or disable a resource.STOP
GP009A write targets a credential, private-key, wallet or environment file.STOP
GP010An argument contains a known secret-key format.STOP
GP011Permissions would allow every local user to write and execute the target.STOP
GP012A command would turn off or flush a local firewall.STOP
GP013A write targets host resolution or sudo authorization settings.STOP
GP014A package install uses a URL or Git source rather than a registry version.STOP

These checks cover recognizable command forms, not every equivalent shell program. Main and master are the shared branches recognized for hard reset. If branch context cannot be established, guard_branch_unknown selects shadow. Encoded commands, aliases and programs that construct commands later can bypass pattern checks. A clean result is not proof of safety.

Tool rules

Fields in each toolRules entry
FieldValue and behavior
patternRequired tool-name regular expression, up to 512 characters. Applies to the full name, such as mcp__documents__save_document.
capabilityCapability classification for matching calls. Rules cannot lower the built-in classification of a file write or payment-like call.
requiredCapabilityMinimum capability claim required by the Spend policy evaluator for matching calls.
unitCostCentsNon-negative safe integer charged once per allowed or shadow call for cap accounting. Default: 0. These are operator-configured units, not observed provider charges.

Unpriced tools cost zero in this ledger. The accounting uses a synthetic input-token unit in the Spend pricing path; those token counts are not measured model usage. A configured cap cannot limit unknown third-party charges.

Caps and selectors

Fields in root caps and session caps
FieldValue and behavior
windowRequired: per_call, per_minute, per_hour, per_day or per_month. The plugin's in-memory store uses fixed windows; its month is a 30-day interval, not a calendar month.
amountCentsRequired non-negative safe integer. The cap is exceeded when projected configured spend is greater than this amount.
actionblock, shadow or allow. Default: block. The plugin does not accept the SDK's downgrade action.
selectorOptional object of exact actor-field matches. Every supplied field must match. Omission shares the cap across the policy's tenant scope.
reasonOptional short identifier forwarded as cap metadata to Spend. The plugin's displayed reason is a stable policy reason code. Do not put content or credentials here.
Accepted selector fields
FieldSource and limits
tenantIdThe root tenant identifier, defaulting to local.
agentIdHost agent_id, then the session's configured agent ID, then its session ID. No separate subagent accounting is possible without a distinct identity.
taskIdThe session's matterId or root defaultMatterId.
sessionIdActual host session ID. Every session-level cap is automatically scoped to its session, overriding a supplied session selector.
providerThe MCP server segment of mcp__server__tool, or codex for a local tool. The tool segment becomes the model name.
userIdAccepted by cap validation, but current plugin calls do not populate this actor field. A selector requiring it does not match.
teamIdAccepted by cap validation, but current plugin calls do not populate this actor field. A selector requiring it does not match.

Selector values must be strings. workflowId is not an accepted plugin cap selector. Identical selector and window combinations share spend; adding another cap on that combination does not charge the call twice.

Session fields

Fields in sessions keyed by host session ID
FieldValue and behavior
matterIdSets actor.taskId for this session, overriding defaultMatterId.
agentIdOperator mapping used when the host does not supply agent_id.
allowedToolsAdditional allowlist. When present, a tool must match this list and any root allowlist.
deniedToolsAdditional explicit tool denies. Root denies still apply.
ethicalWallAdditional tool-name wall for this session. Root wall patterns still apply.
maxCapabilityAdditional tier ceiling. It cannot loosen a more restrictive root ceiling.
capsAdditional caps with the same fields as root caps. The runtime adds the actual session ID to each selector.

Sessions do not override the root mode, license, tool rules or payment pattern. Unknown fields are not a way to add a supported feature; use the fields documented here.

Capability tiers

Ordered from least to most privileged
TierClassification
read_onlyDefault classification for other tools, unless a matching payment pattern or tool rule raises it. A tool's name does not prove its real effects.
data_writeBuilt-in minimum for Bash, apply_patch, Edit and Write. Assign this tier to document-writing MCP tools with a rule.
payment_initiateBuilt-in minimum when the configurable payment expression matches the provider and tool/model name.
payment_executeExplicit higher classification available to an operator's tool rule. Classification grants no payment credentials.

capability describes the call's claim, requiredCapability sets the policy's minimum claim, and maxCapability places a ceiling on a session. Tool allowlists and ethical walls remain independent checks.

A matter budget and an ethical wall

This example uses synthetic identifiers and illustrative configured amounts. Replace the session key with a real host session ID. The document save costs two configured cents, the matter cap is 500 cents per day, and the session cap is 50 cents per day. Free enforces the same local rules and records signed decisions. An explicit shadow policy records without blocking.

{
  "version": 1,
  "tenantId": "example-firm",
  "mode": "enforce",
  "hookBudgetMs": 250,
  "defaultMatterId": "matter-example",
  "maxCapability": "data_write",
  "allowedTools": ["^mcp__documents__(search_documents|read_document|save_document)$", "^mcp__agentguard__(get_status|list_decisions|verify_chain|export_receipts)$", "^update_plan$"],
  "deniedTools": ["^Bash$"],
  "ethicalWall": ["^mcp__restricted_matter__.*$"],
  "toolRules": [{"pattern": "^mcp__documents__save_document$", "capability": "data_write", "unitCostCents": 2}],
  "caps": [{"selector": {"taskId": "matter-example"}, "window": "per_day", "amountCents": 500, "action": "block"}],
  "sessions": {
    "session-example": {
      "matterId": "matter-example",
      "agentId": "reviewer-example",
      "ethicalWall": ["^mcp__other_matter__.*$"],
      "caps": [{"window": "per_day", "amountCents": 50, "action": "block"}]
    }
  }
}

For a read-only review, set that session's maxCapability to read_only and limit its allowedTools to document reads, planning and the desired AgentGuard read-only tools. Do not silently exempt plugin tools from an allowlist.

An ethical wall is a list of tool-name patterns, not a content classifier or a conflicts database. Pair it with service-side permissions. A request blocked by a policy does not authorize the agent to loosen that policy.

Records and durability

The signed chain is ${PLUGIN_DATA}/ledger/decisions.ndjson. Decisions record tool names, input SHA-256, byte counts, actor identifiers, configured cost and the policy result. They never retain tool input content or output text. Tool names and identifiers can still be sensitive metadata, so protect the policy, private signing key and records.

Outcome receipts link to the preceding decision ID. Host timing is preferred; otherwise duration is elapsed pre/post time, including scheduling. Explicit errors and structured exit codes determine success. Codex 0.154 unified Bash provides raw output without an exit code, so those receipts use status: "unknown" and success: null. The plugin does not parse output text to guess success.

Before replying, the worker signs and writes each complete plugin ledger row to the operating system; a later asynchronous sync confirms a durable chain head, so a crash or power loss can lose an unconfirmed tail. On restart it verifies surviving rows against that head, discards only an incomplete final row beyond it, and appends a signed integrity event when surviving evidence shows an unconfirmed tail or sync failure; complete invalid rows are never silently repaired.

An older ledger without a durability checkpoint receives one conservative integrity event at first startup. Existing signed rows remain intact. Status counts integrity events separately from tool decisions and configured spend.

Status, verification and export

Use the agentguard-status skill for an operator summary or the optional local MCP tools below. These tools are read-only: they do not change policy, execute business tools or write an export file.

Optional read-only MCP tools
ToolResult
get_statusTier, seats used, limit, storage verification, expiry, effective mode and reason; UTC-day decisions, configured spend, blocks, outcomes, integrity events and fail-open health. Supply the known sessionId when available.
list_decisionsA bounded page of content-free decision summaries, using fromSequence and limit.
verify_chainVerifies hashes and signatures against the local public verification key. Available on every tier.
export_receiptsReturns a bounded page of signed receipts and the public key when a usable paid license is present. The caller saves the bundle.

Fail-open health reports count, total and rate over the last hour and since worker start. Each pre-tool gate invocation counts once, including a pass-through allow from the gate that does not govern that tool. Post-tool observations are separate. A timeout and late worker response share one request ID. Either rate above five percent produces a one-line warning with a known cause.

Health counters are unsigned operational observations and may lag the signed chain. A partial or truncated denominator is labeled. Signed UTC-day failure counts remain separate from pending recovery rows, which can span dates and contain batch duplicates. Never add pending counts to the verified total.

For a custodian bundle, use agentguard-verify or this paid local export helper with an operator-approved destination:

node "${PLUGIN_ROOT}/runtime/verify.cjs" export RECEIPTS_FILE

Preserve the verification public key through a trusted channel separate from the bundle. A bundled key proves consistency with that key, not who controlled it. Export never includes the signing private key.

To disable only the optional MCP server for the public marketplace installation:

[plugins."agentguard@agentguard".mcp_servers.agentguard]
enabled = false

Enterprise installation

For a firm using Astra for Law in Codex or ChatGPT Work, policy can assign matter budgets, tool allowlists for a review session and ethical-wall denies. The firm retains the signed content-free record locally. These controls describe tool authorization and recorded activity, not legal analysis or model accuracy.

Reviewed trust through config

IT reviews one installation through /hooks, then uses scripts/print-trust-state.cjs to print exact hooks.state keys and trusted_hash values for its marketplace. Distribute the reviewed TOML through managed configuration.

Managed hooks through MDM

Distribute the reviewed package, Node 22 and dependencies. Configure requirements.toml with allow_managed_hooks_only = true, hooks.managed_dir and all five hook commands. MCP and skills remain installed through the private marketplace.

A changed normalized hook definition changes its hash and prompts review. Referenced script bytes and imported modules are not part of that hash, so IT must protect the reviewed executable files separately. Managed commands need explicit PLUGIN_ROOT and per-user PLUGIN_DATA paths.

On macOS, Codex managed preferences use application identifier com.openai.codex and the keys config_toml_base64 or requirements_toml_base64. The enterprise guide includes complete TOML, packaging commands and source citations pinned to rust-v0.154.0. The guide records the hook definitions and hashes checked through the normal review flow; managed-device delivery and Windows execution have not been verified.

MDM delivery does not turn fail-open hooks into fail-closed authorization. Enforce a fail-closed requirement at the tool or service boundary.

Read the enterprise installation guide

Source and package reference

The public repository contains the plugin, generated compatibility installation, tests and changelog. The portable manifest uses OpenAI extension settings, the MCP transport is local stdio, and the empty app mapping claims no registered external server. The plugin itself is not published to npm.

The package uses the same license terms as Spend. Return to the plugin overview or the SDK documentation.