{"id":381248,"date":"2026-10-14T01:14:51","date_gmt":"2026-10-14T01:14:51","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/agent-tollgate\/"},"modified":"2026-10-14T01:14:35","modified_gmt":"2026-10-14T01:14:35","slug":"belayline-agent-governance","status":"publish","type":"plugin","link":"https:\/\/ko.wordpress.org\/plugins\/belayline-agent-governance\/","author":15259657,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.4.5","stable_tag":"0.4.5","tested":"7.1.3","requires":"7.0","requires_php":"8.1","requires_plugins":null,"header_name":"Belayline Agent Governance","header_author":"Stephen Paul Samynathan","header_description":"Governs other plugins' agent tool calls: verb-tier gating, capability packs, human approval with a state-fingerprint check, and a tamper-evident audit log. Ships a WordPress-core module plus a WooCommerce integration module.","assets_banners_color":"0e292e","last_updated":"2026-10-14 01:14:35","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/github.com\/stephen1204paul\/belayline-agent-governance","header_author_uri":"https:\/\/profiles.wordpress.org\/stephen1204paul\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":57,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.4.5":{"tag":"0.4.5","author":"stephen1204paul","date":"2026-10-14 01:14:35","revision":3745134}},"upgrade_notice":{"0.4.5":"<p>Renamed to Belayline Agent Governance, with a new slug, text domain and <code>belayline_<\/code> prefix for hooks, options and tables. Deactivate and delete the old plugin folder, then install this one.<\/p>","0.4.4":"<p>Security fix: order status values with WooCommerce&#039;s wc- prefix (for example wc-completed) now get the same approval tier as unprefixed ones. Update right away.<\/p>","0.4.3":"<p>Renamed (new folder and main file). If you installed an earlier build from GitHub, deactivate and delete the old plugin folder, then install this one. Tables and settings carry over.<\/p>","0.4.2":"<p>Fixes a critical error on sites without mcp-adapter the first time a pack allowed an ability call. Update right away.<\/p>","0.4.1":"<p>Renamed for the WordPress.org directory; no behaviour change. If you installed 0.4.0 from GitHub, deactivate and delete the old agent-safety folder, then install this one. Tables and settings carry over.<\/p>","0.4.0":"<p>Schema change: the grants table is renamed and the approvals table gains new columns. Back up\nyour database before upgrading. There is no downgrade path back to a pre-0.4.0 build once this\nruns.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3745134,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3745134,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3745134,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3745134,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.4.5"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[2353,8534,242115,600,286],"plugin_category":[45,54],"plugin_contributors":[235985],"plugin_business_model":[],"class_list":["post-381248","plugin","type-plugin","status-publish","hentry","plugin_tags-ai","plugin_tags-audit-log","plugin_tags-mcp","plugin_tags-security","plugin_tags-woocommerce","plugin_category-ecommerce","plugin_category-security-and-spam-protection","plugin_contributors-stephen1204paul","plugin_committers-stephen1204paul"],"banners":{"banner":"https:\/\/ps.w.org\/belayline-agent-governance\/assets\/banner-772x250.png?rev=3745134","banner_2x":"https:\/\/ps.w.org\/belayline-agent-governance\/assets\/banner-1544x500.png?rev=3745134","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/belayline-agent-governance\/assets\/icon-128x128.png?rev=3745134","icon_2x":"https:\/\/ps.w.org\/belayline-agent-governance\/assets\/icon-256x256.png?rev=3745134","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>Belayline Agent Governance governs other plugins' agent tool calls, not just its own. It sits between AI\nagents (through the WordPress Abilities API, and through MCP servers built on\n<a href=\"https:\/\/github.com\/WordPress\/mcp-adapter\">mcp-adapter<\/a>, as WooCommerce's is) and the actions\nthose agents can take on your site. Every governed call is classified by tier, checked against a\ncapability pack bound to the calling identity, and written to a tamper-evident, hash-chained\naudit log, before it runs.<\/p>\n\n<p>The plugin is WordPress-general. It ships a <code>core\/*<\/code> module that runs on every site, and a\nWooCommerce module that wires in automatically when WooCommerce is active. Neither one trusts a\ntool's own \"read-only\" or \"safe\" label; classification comes from the plugin's own policy map.<\/p>\n\n<p><strong>Capability packs and tiers<\/strong><\/p>\n\n<p>Every governed ability maps to a tier: reversible (read), side-effecting (write), or\nirreversible (delete, refund, cancel). A capability pack is a named, scoped view of the verb\ncatalog for one identity \u2014 a WordPress application password, a user or role, or a WooCommerce\nREST API key \u2014 with an allow list, a hard-denied tier class, a per-tier approval requirement, and\noptional rate and spend limits. A credential with no explicit binding falls back to a safe\ndefault pack rather than inheriting full access.<\/p>\n\n<p><strong>Human approval<\/strong><\/p>\n\n<p>When a pack requires approval for a tier (the starter packs do for irreversible calls), a call\nat that tier doesn't execute. It is queued under <strong>Tools \u2192 Pending Agent Actions<\/strong> for a human\nto approve or reject. When an\nApproval is requested, Belayline Agent Governance captures a fingerprint of the target's current state; when\nthe approval is claimed, it re-checks that fingerprint. A mismatch means the target changed\nbetween request and claim, so the old approval is marked stale and a fresh request is filed. This\nnarrows the gap between a human's approval and the write \u2014 it does not close it, because the\nshort window between claiming an approval and executing the call still exists.<\/p>\n\n<p><strong>Self-service approval status<\/strong><\/p>\n\n<p>An agent can call <code>belayline\/check-approval<\/code> with an <code>approval_id<\/code> to poll its own request\nwithout a human in the loop. It reports a reduced, public status (pending, approved, rejected,\nexpired, used, or superseded) and a plain-English <code>next_action<\/code>, and never returns the approver's\nidentity, the original arguments, or any token.<\/p>\n\n<p><strong>Hash-chained audit log<\/strong><\/p>\n\n<p>Every gate decision and executed action is appended to an append-only, hash-chained log under\n<strong>Tools \u2192 Agent Audit Log<\/strong>, with a tamper-evidence check that re-verifies the chain on load.<\/p>\n\n<p><strong>Core coverage<\/strong><\/p>\n\n<p>Governs the three abilities WordPress core registers today. Any other <code>core\/*<\/code> ability is denied\nuntil Belayline Agent Governance maps it, so future core write abilities fail closed rather than run\nungoverned.<\/p>\n\n<p><strong>WooCommerce coverage<\/strong><\/p>\n\n<p>When WooCommerce is active, Belayline Agent Governance governs exactly these 16 abilities:<\/p>\n\n<ul>\n<li><code>woocommerce\/products-list<\/code><\/li>\n<li><code>woocommerce\/products-get<\/code><\/li>\n<li><code>woocommerce\/products-create<\/code><\/li>\n<li><code>woocommerce\/products-update<\/code><\/li>\n<li><code>woocommerce\/products-delete<\/code><\/li>\n<li><code>woocommerce\/products-query<\/code><\/li>\n<li><code>woocommerce\/product-create<\/code><\/li>\n<li><code>woocommerce\/product-update<\/code><\/li>\n<li><code>woocommerce\/product-delete<\/code><\/li>\n<li><code>woocommerce\/orders-list<\/code><\/li>\n<li><code>woocommerce\/orders-get<\/code><\/li>\n<li><code>woocommerce\/orders-create<\/code><\/li>\n<li><code>woocommerce\/orders-update<\/code><\/li>\n<li><code>woocommerce\/orders-query<\/code><\/li>\n<li><code>woocommerce\/order-add-note<\/code><\/li>\n<li><code>woocommerce\/order-update-status<\/code><\/li>\n<\/ul>\n\n<p>Everything else under <code>woocommerce\/<\/code> is refused as an unknown verb. Deleting a product with\n    force: true is classed irreversible; a plain delete (trash) is side-effecting.<\/p>\n\n<p><strong>Which MCP endpoint to use<\/strong><\/p>\n\n<p>WooCommerce's own MCP endpoint (<code>\/wp-json\/woocommerce\/mcp<\/code>) is deprecated by WooCommerce itself,\nwhich points clients at the shared <code>mcp-adapter<\/code> server instead. Belayline Agent Governance governs both the\nsame way, through the ability permission check, but the two endpoints don't expose the same\ninformation to an agent:<\/p>\n\n<ul>\n<li>On the shared <code>mcp-adapter<\/code> server, an agent that hits <code>approval_required<\/code> gets the full error\ndata, including <code>approval_id<\/code>, and can call <code>belayline\/check-approval<\/code> to poll it.<\/li>\n<li>On WooCommerce's own endpoint, the bundled <code>mcp-adapter<\/code> 0.3.0 only forwards the error message\ntext to the client, not the structured error data. An agent sees the message \"needs human\napproval\" but not the <code>approval_id<\/code>, and <code>belayline\/check-approval<\/code> isn't reachable on that\ntransport at all \u2014 the approval still exists and is visible on the Pending Actions page, but\nthe agent can't self-serve it.<\/li>\n<\/ul>\n\n<p>We recommend running agents against the shared mcp-adapter server. WooCommerce's own endpoint is\nsupported for as long as WooCommerce ships it, with the limitation above.<\/p>\n\n<p><strong>Environment awareness<\/strong><\/p>\n\n<p>Belayline Agent Governance notices when a site's address changes (a migration, a staging clone) and treats it\nas a new environment: shadow-mode windows, active grants, and approved-but-unclaimed approvals\nare all voided rather than carried over silently, and an admin notice stays up until someone\nrebinds the site from the settings page. Production sites (<code>wp_get_environment_type() ===\n'production'<\/code>, WordPress's own default) never default to shadow mode and cap any shadow window at\n24 hours.<\/p>\n\n<p><strong>Multisite<\/strong><\/p>\n\n<p>WordPress multisite is not supported. Activation is refused on a multisite install, network-wide\nor per site.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin can send data outside your site in two ways.<\/p>\n\n<p><strong>Email.<\/strong> When a new agent action needs approval, Belayline Agent Governance sends an email through\n    wp_mail() to the site's admin email address, or to a recipient set on the Pending Agent\nActions screen, linking to the login-protected review screen. It goes through your site's own\nmail delivery.<\/p>\n\n<p><strong>Webhook (opt-in).<\/strong> An administrator can set a webhook URL on the Pending Agent Actions\nscreen. When\nset, every new pending approval sends an HTTP POST to that URL with a JSON body containing the\nevent name, the approval id, the verb (ability) that was called, and a link to the review screen.\nNo call arguments are sent. The webhook is off until an administrator enters a URL, and the\ndestination is the site operator's choice. Nothing is sent to the plugin's author.<\/p>\n\n<h3>Privacy<\/h3>\n\n<p>Belayline Agent Governance keeps a hash-chained audit log of gate decisions and executed agent actions,\nincluding the identity that made the call. A personal-data export for a WordPress user includes\ntheir matching audit rows. An erasure request does not delete them: they are reported as\nretained, with a note that they're kept as a tamper-evident security record, because rewriting a\nrow would break the hash chain that proves the log hasn't been tampered with. Matching is done by\nresolving the requester's email to a WordPress user and matching rows on that user only; recorded\ntool inputs are never searched for personal data, but they may contain it, since they can include\ndata handled by other plugins (customer records, order details) that the calling agent's tool\ntouched.<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin to <code>\/wp-content\/plugins\/belayline-agent-governance<\/code>, or install it through the Plugins\nscreen.<\/li>\n<li>Activate <strong>Belayline Agent Governance<\/strong>.<\/li>\n<li>Visit <strong>Tools \u2192 Agent Capability Packs<\/strong> to bind identities (application passwords, users or\nroles, WooCommerce REST API keys) to a pack. Unbound credentials get a safe default pack.<\/li>\n<li>Review pending actions under <strong>Tools \u2192 Pending Agent Actions<\/strong> and the log under <strong>Tools \u2192\nAgent Audit Log<\/strong>.<\/li>\n<\/ol>\n\n<p>WooCommerce support activates automatically when WooCommerce is active; there is nothing else to\nconfigure for it to be safe by default.<\/p>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20require%20woocommerce%3F\"><h3>Does this require WooCommerce?<\/h3><\/dt>\n<dd><p>No. The plugin is WordPress-general and governs the core Abilities API on any site. WooCommerce\nsupport is a self-contained module that loads only when WooCommerce is active.<\/p><\/dd>\n<dt id=\"does%20it%20work%20on%20multisite%3F\"><h3>Does it work on multisite?<\/h3><\/dt>\n<dd><p>No. Activation is refused on a multisite install, both network-wide and per site.<\/p><\/dd>\n<dt id=\"what%20about%20woocommerce%27s%20own%20mcp%20endpoint%3F\"><h3>What about WooCommerce's own MCP endpoint?<\/h3><\/dt>\n<dd><p>It's governed the same way as the shared mcp-adapter server, but WooCommerce's bundled\nmcp-adapter version doesn't forward the structured <code>approval_required<\/code> error data to the client,\nso an agent on that endpoint can see the approval message but not the <code>approval_id<\/code>, and can't\nreach <code>belayline\/check-approval<\/code>. See the Description for details. We recommend the shared\nmcp-adapter server; WooCommerce's own endpoint is supported while WooCommerce continues to ship\nit.<\/p><\/dd>\n<dt id=\"what%20happens%20to%20my%20data%20on%20uninstall%3F\"><h3>What happens to my data on uninstall?<\/h3><\/dt>\n<dd><p>Nothing, by default. Deleting the plugin from the Plugins screen leaves the audit log, approval\ntables, and plugin options in place, because the audit log is a tamper-evident security record.\nTo have uninstall remove everything (all three tables, every option, every scheduled cron event),\ndefine <code>BELAYLINE_REMOVE_DATA<\/code> as <code>true<\/code> (for example in <code>wp-config.php<\/code>) before deleting the\nplugin.<\/p><\/dd>\n<dt id=\"how%20are%20ai%20agents%20identified%3F\"><h3>How are AI agents identified?<\/h3><\/dt>\n<dd><p>By whichever credential authenticated the request: a WordPress application password, a logged-in\nuser or their role, or \u2014 when WooCommerce is active \u2014 the WooCommerce REST API key used to\nauthenticate the request. Each is bound to a capability pack independently under <strong>Tools \u2192 Agent\nCapability Packs<\/strong>.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.4.5<\/h4>\n\n<ul>\n<li>Renamed to Belayline Agent Governance: new slug and text domain <code>belayline-agent-governance<\/code>, and a <code>belayline_<\/code> prefix for hooks, options and tables.<\/li>\n<\/ul>\n\n<h4>0.4.4<\/h4>\n\n<ul>\n<li>Security: order status values with WooCommerce's wc- prefix now get the same approval tier as unprefixed ones.<\/li>\n<li>Refuses to load beside the old Agent Safety plugin instead of causing a fatal error, and asks you to deactivate the old one.<\/li>\n<\/ul>\n\n<h4>0.4.3<\/h4>\n\n<ul>\n<li>Renamed: new slug, text domain and main file. Data, settings\nand hooks are unchanged. If you installed an earlier build from GitHub, deactivate and delete the\nold plugin folder, then install this one; tables and settings carry over.<\/li>\n<li>Fixed: rate limits, tripwires and daily spend caps could be exceeded under concurrent requests,\nbecause their counters were read-modify-write transients. Counters now live in their own table and\neach change is a single atomic statement; a call is counted before it is checked.<\/li>\n<li>The multisite and grants-table notices now show only to users who can manage options.<\/li>\n<\/ul>\n\n<h4>0.4.2<\/h4>\n\n<ul>\n<li>Fixed: on a site without mcp-adapter, the first ability call a capability pack allowed ended in a critical\nerror, because the audit log reached for the MCP request handler, which needs mcp-adapter's interface.\nDenied calls were not affected.<\/li>\n<\/ul>\n\n<h4>0.4.1<\/h4>\n\n<ul>\n<li>Renamed for the WordPress.org directory: new slug, text domain and main file.<\/li>\n<li>No behaviour change. Data, settings and hooks are unchanged.<\/li>\n<li>If you installed 0.4.0 from GitHub, deactivate and delete the old <code>agent-safety<\/code> folder, then\ninstall this one. Deleting the old plugin keeps your tables and settings unless\n  BELAYLINE_REMOVE_DATA is defined as true in <code>wp-config.php<\/code>, so they carry over.<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li><code>core\/*<\/code> module narrowed to the three abilities WordPress core actually registers\n(<code>get-site-info<\/code>, <code>get-user-info<\/code>, <code>get-environment-info<\/code>); any other <code>core\/*<\/code> ability is now\nrefused as an unknown verb instead of matching a speculative merge-proposal verb.<\/li>\n<li>WooCommerce module narrowed to the 16 abilities WooCommerce 11 ships; forward-compatibility\nentries for abilities WooCommerce hasn't shipped yet are removed, and anything else under\n  woocommerce\/ is refused.<\/li>\n<li><code>woocommerce\/product-delete<\/code> is now side-effecting, and irreversible only with <code>force: true<\/code>,\nmatching <code>products-delete<\/code>.<\/li>\n<li>Added a state fingerprint on Approvals: the target's revision marker is captured at request\ntime and re-checked at claim time. A mismatch marks the old approval stale and files a fresh\none. This narrows the window between a human's approval and the write; it does not close it.<\/li>\n<li>Added environment awareness: a site-address change voids shadow windows, grants, and\nunclaimed approvals rather than carrying them over silently, with an admin notice until the\nsite is rebound.<\/li>\n<li>Added a self-service approval check: <code>belayline\/check-approval<\/code> lets an agent poll the\nstatus of its own pending request.<\/li>\n<li>Directory-compliance pass: i18n on every user-facing string, an opt-in uninstall that now\ncovers every option and cron event, disclosed audit-row retention through erasure requests\ndocumented in the exporter and privacy policy, and the grants table renamed to\n  belayline_grants.<\/li>\n<li>Bumped minimum requirements to WordPress 7.0 and PHP 8.1.<\/li>\n<li>No downgrade path: the grants table was renamed as part of this release's schema change, so a\n0.3.x build cannot read a 0.4.0 database.<\/li>\n<\/ul>","raw_excerpt":"Governs other plugins' agent tool calls, with human approval and a tamper-evident audit log.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/381248","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=381248"}],"author":[{"embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/stephen1204paul"}],"wp:attachment":[{"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=381248"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=381248"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=381248"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=381248"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=381248"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/ko.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=381248"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}