Jarvis Agent Role
Version-controlled 'Jarvis' admin-minus WordPress role for the AI agent user. Distributed via Git Updater.
by Code Atlantic · github.com/code-atlantic/jarvis-agent-role · website
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/code-atlantic/jarvis-agent-role/releases/download/1.7.1/jarvis-agent-role-1.7.1.zipDeclares an update source (code-atlantic/jarvis-agent-role), so updates arrive through the plugin's own updater.
Readme
Jarvis Agent Role
A self-contained WordPress plugin that provisions a dedicated, least-privilege user for an
AI agent — a version-controlled jarvis_agent role, an auto-created agent user with a local
avatar, a tamper-aware audit trail of every agent action, and an optional read-only SQL
ability. Built for sites driven by MCP / REST tooling, where the agent needs broad content
powers but must never be able to execute code or escalate accounts.
Fork it, rename the identity via filters (or in code), and you have a hardened agent account for your own AI integration.
What you get
jarvis_agentrole, defined in code. The capability list injarvis-agent-role.phpis the single source of truth. BumpROLE_VERSIONand the stored role rebuilds on the next admin load — no deactivate/reactivate dance.- A provisioned agent user. Activation creates a
jarvisuser on the role, with display name, bio, and a bundled avatar served locally everywhere WordPress renders one — no Gravatar dependency. Login, email, and bio are all filterable (see Configuration); set a real email for your site. - An audit log of every Abilities-API tool call, auth event, and WP-CLI command, written as JSON lines to a file outside the web root.
- An optional read-only SQL ability (
jarvis/execute-sql) for ad-hoc reporting, off by default and triple-gated when on.
Security model: "Admin-minus"
The agent gets everything content/MCP tooling exercises — including manage_options —
but none of the capabilities that allow code execution or account escalation.
Deliberately withheld:
install_plugins,activate_plugins,edit_plugins,edit_themes,edit_filesupdate_core,update_plugins,update_themescreate_users,edit_users,delete_users,promote_usersunfiltered_html,export,import
A leaked credential can edit content and settings — it cannot run arbitrary PHP or take over accounts. Auth is via application passwords (revocable, per-site), never an interactive login: the account is created with an unused cryptographically random password.
Custom post types — derived, not hand-listed
CPTs often register custom capability types, so generic edit_posts doesn't cover them.
Rather than maintain a hand-list that rots, derive_cpt_capabilities() walks post,
page, and every show_in_rest post type and pulls each one's mapped
edit_posts / edit_others_posts / edit_published_posts / publish_posts cap names off
its capability object — e.g. a popup CPT yields edit_popups, a download CPT with
capability_type => 'product' yields edit_products, automatically.
Because activation can run before other plugins register their CPTs, missing derived caps
are re-asserted on every admin_init (a cheap no-op once present). A new CPT appears →
the next admin load grants its caps. Delete-class primitives are deliberately not derived.
The stock list also includes capabilities for Easy Digital Downloads, Popup Maker,
FluentCRM, and FluentBoards — trim those in capabilities() (or via the filter below)
if your stack differs.
Installation & provisioning
-
Copy this folder into
wp-content/plugins/and activate (orwp plugin activate jarvis-agent-role). Activation registers the role, then creates the agent user on it if it doesn't already exist. Idempotent — re-activating won't duplicate or clobber. -
Generate an application password for the agent:
wp user application-password create jarvis "jarvis-mcp" --porcelainStore the returned token in your MCP/agent config for that site. Rotate by deleting and re-creating the application password — the role and other sites are untouched.
wp jarvis user re-runs the provisioning check on demand (and prints the app-password
command).
Set your own agent identity
The defaults are deliberately generic: login jarvis, email jarvis@example.com
(RFC 2606 reserved — it can never be registered), and a neutral bio. Set your own in
wp-config.php — this is the recommended approach, and it keeps your real agent
identity out of the plugin and out of version control:
// wp-config.php
define( 'JARVIS_USER_LOGIN', 'friday' );
define( 'JARVIS_USER_EMAIL', 'agent@yourcompany.com' );
define( 'JARVIS_USER_BIO', 'Our AI agent. Scoped permissions, fully logged.' );
Equivalent filters (jarvis_user_login, jarvis_user_email, jarvis_user_bio) are
available for programmatic cases and take precedence over the constants.
Set these before first activation so the user is created with them. If the account
already exists, run wp jarvis user to re-assert display name, nickname, and bio.
Changing the login after creation does not rename the existing account — provisioning
then looks for a different user and will refuse to adopt one that isn't unambiguously
the agent (see below); rename the account in WordPress instead.
The agent account never logs in interactively — it's created with an unused random password and authenticates via application password — so a predictable login is not itself a weakness. The security boundary is the capability ceiling and the revocable credential, not the username.
Safety: account adoption rules
- A pre-existing account is adopted only when both the login and the email match
the canonical agent identity and resolve to the same user — i.e. the account you'd have
created yourself (e.g. earlier via
wp user create). It's then flagged, given the role, and its identity re-asserted. - If the login or the email collides with a different account, provisioning
refuses — it will not bolt an agent role onto a real person's user. Change the
identity via the
jarvis_user_login/jarvis_user_emailfilters, or flag the intended account manually (wp user meta update <id> _jarvis_agent_user 1) and runwp jarvis user.
Public authorship for agent-assisted content
Set a designated human author when Jarvis prepares site content:
wp option update jarvis_public_author_login danieliser
Or define JARVIS_PUBLIC_AUTHOR_LOGIN in wp-config.php. When Jarvis saves a public
post, page, or public custom post type, the plugin assigns that WordPress user as the
real post_author and stores a _jarvis_assisted marker. Themes, feeds, REST responses,
author archives, and SEO schema therefore all receive the human author naturally rather
than through presentation-only filters.
On singular frontend views, marked content receives a short disclosure before the article. Block content gets a Paragraph block; classic content gets equivalent semantic HTML. Customize the behavior with:
jarvis_public_author_login— filter the configured public-author login.jarvis_public_author_supports_post_type— opt a post type in or out.jarvis_author_disclosure_text— customize the disclosure text.
Audit log
Captured at the Abilities API layer: WordPress fires
wp_before_execute_ability / wp_after_execute_ability around every ability execution,
so one pair of hooks logs every MCP/agent tool call — core mcp-adapter, third-party
adapters, and any custom tool added later — without touching core or vendor files.
Auth events (cookie login + application-password) are captured separately.
- Ability and auth logging fires only for
jarvis_agentusers — humans and other API clients log nothing there. - WP-CLI commands are logged unconditionally (not gated on the role): every
wpcommand is privileged shell access worth auditing, and it usually runs with no--user. Each entry records the command, the OS user, and the SSH client, so the trail ties back to who was on the box. - Each
abilityentry records the tool name, full input args, andok/errorstatus. Result bodies are not logged (inputs already capture what changed; read payloads are noise). Credential-shaped keys (password,token,secret,api_key, …) are redacted so the log never becomes a secret sink. Oversized inputs are clipped at 20 KB.
Location & usage
Defaults to jarvis-audit/jarvis-audit.log one level above wp-content (typically
above the web root, so the trail isn't web-reachable) when writable; otherwise falls back
to wp-content/jarvis-audit.log. Override explicitly:
// wp-config.php
define( 'JARVIS_AUDIT_LOG_FILE', '/var/log/jarvis/audit.log' );
wp jarvis audit --lines=50 # last 50 events (prints the resolved path too)
- Rotates at 10 MB (keeps one
.1backup). Self-bounding; no cleanup needed. - Writes fail silently by design — auditing never breaks the request it observes.
- Not tamper-proof: a
manage_optionscredential could reach a log inside the tree. For a defensible trail, pointJARVIS_AUDIT_LOG_FILEsomewhere the web user can append but not read/rewrite, or ship lines to an external sink.
Read-only SQL ability (optional, off by default)
jarvis/execute-sql (WP 6.9+ Abilities API) runs a single read-only query and returns
rows — the deliberate, scoped answer to "let the agent run ad-hoc reports". Three gates:
-
Identity, not capability: only the flagged agent user may call it — another admin with
manage_optionscannot. -
Statement validation: leading-keyword allowlist (
SELECT/SHOW/DESCRIBE/EXPLAIN/WITH), a mutating-keyword denylist, no statement stacking, and an injectedLIMIT(1,000-row hard cap). -
Explicitly enabled per site:
// wp-config.php define( 'JARVIS_ENABLE_SQL', true );
When disabled (the default) the ability isn't even registered.
Configuration reference
| Hook / constant | Purpose |
|---|---|
jarvis_role_capabilities (filter) |
Add/drop caps per site without forking — e.g. revoke manage_options on a hardened install. |
JARVIS_USER_LOGIN / JARVIS_USER_EMAIL / JARVIS_USER_BIO (constants) |
Agent account identity. Recommended — set in wp-config.php. |
jarvis_user_login / jarvis_user_email / jarvis_user_bio (filters) |
Same values, programmatically. Take precedence over the constants. |
JARVIS_PUBLIC_AUTHOR_LOGIN (constant) / jarvis_public_author_login (option and filter) |
Human WordPress user assigned as the real author of Jarvis-assisted public content. |
jarvis_public_author_supports_post_type (filter) |
Opt post types in or out of public-author reassignment. |
jarvis_author_disclosure_text (filter) |
Customize the frontend AI-assistance note. |
jarvis_enable_sql (filter) / JARVIS_ENABLE_SQL (constant) |
Enable the SQL ability. |
JARVIS_AUDIT_LOG_FILE (constant) |
Explicit audit-log path. |
WP-CLI commands
| Command | Purpose |
|---|---|
wp jarvis sync |
Force a role rebuild from the capability array. |
wp jarvis user |
Ensure the agent user exists and its identity is current. |
wp jarvis audit --lines=N |
Tail the audit trail. |
Updates (Git Updater)
The main file carries GitHub Plugin URI / Primary Branch headers, so any site with
Git Updater installed sees new tagged releases as ordinary
plugin updates.
Cutting a release: bump the Version: header (and ROLE_VERSION if capabilities
changed), commit, then push a matching tag:
git tag 1.6.2 && git push origin 1.6.2
Updating permissions later
- Edit the
capabilities()array injarvis-agent-role.php. - Bump
ROLE_VERSIONand theVersion:header. - Commit + push a matching tag. The role re-syncs on the next admin load, or force it
with
wp jarvis sync.
Deactivation & uninstall
Deactivation removes the role and resets the version marker; users on the role fall back to no role — reassign them first on a live site. The agent user (and its application passwords) is intentionally left intact on deactivation, so a simple toggle never breaks a live integration.
License
GPL-2.0-or-later.
Read the full README on GitHub →