WP-CLI Commands

Every wp captaincore command the Manager registers, what it does and how to run it.

CaptainCore Manager registers a set of wp captaincore commands for scheduled and operational work. They run against the WordPress install that hosts the Manager, not against managed sites, so pass --path when you call them from cron.

Every command is registered in captaincore.php and implemented by a class in app/. Most load after WordPress (@when after_wp_load).

Fleet operations

provider-sync

Reconciles local SFTP and SSH credentials with each site’s hosting provider. It crawls sites slowly, calls Site::remote_sync() on each, and updates drifted address, port, username, password and home directory values from the provider’s API. Sites whose provider does not implement remote sync are marked skipped. The last successful sync timestamp is written to wp_captaincore_sites.details.last_remote_sync_at, so --stale lets a re-run skip recently checked sites and resume naturally after an interruption.

Options: [--provider=<provider>], [--site=<site_id>], [--throttle=<seconds>] (default 3), [--limit=<n>], [--stale=<duration>] accepting values like 6d, 12h or 30m, [--dry-run] and [--verbose].

wp captaincore provider-sync --provider=kinsta --stale=6d

Good cron candidate. Use --throttle and --limit to keep provider APIs happy.

remote

A generic passthrough to the CaptainCore\Remote\* provider wrappers. Each wrapper handles auth and the base URL, so this command lets you hit any provider endpoint from the shell without writing a one-off wp eval.

Arguments: <provider> matched case-insensitively against the classes in app/Remote (for example spaceship, kinsta, mailgun, gridpane, forward-email, google-web-risk), <method> which must be get, post, put or delete, and <endpoint> relative to the provider’s base URL. Pass a request body with [--body=<json>], or as individual --key=value flags when --body is not given. [--format=<format>] accepts json (the default), table, csv or yaml. Running it with an unknown provider prints the available list.

wp captaincore remote spaceship get domains --take=100 --skip=0 --orderBy=expirationDate
wp captaincore remote spaceship put domains/example.com/autorenew --body='{"isEnabled":true}'

Interactive tool, not a cron job.

site-label

Manages status labels on a site. Labels are stored as a structured list under the site’s details.labels JSON. They are display-only badges such as “Moved to Squarespace”, “DNS at Cloudflare” or “Domain expiring”, and they do not change the site’s lifecycle status column.

list takes <site> (a site ID or a domain name) and [--format=<format>].

add takes <site>, a required --type=<type> from moved, dns-elsewhere, not-wordpress, down, domain-expiring, domain-expired or note, and a required --text=<text>. Optional flags are [--evidence=<evidence>], [--source=<source>] accepting manual or monitor-check (default manual), [--color=<color>], [--icon=<icon>], [--detected-at=<datetime>] and [--allow-duplicate]. It is idempotent for automated callers: a label with the same type and source is replaced rather than duplicated, unless you pass --allow-duplicate.

remove takes <site> and a <label_id> from site-label list, or the literal all to clear every label.

wp captaincore site-label add thorsenconsulting.com --type=moved \
  --text="Moved to Squarespace" --source=monitor-check \
  --evidence="NS=domaincontrol.com; www CNAME ext-sq.squarespace.com; wp-json 404"

DNS and email

dns

Reads and writes DNS records for a domain through the Manager’s DNS backend.

list takes <domain>, plus [--type=<type>] filtering on A, AAAA, CNAME, MX, TXT, SRV, SPF, ANAME or HTTP, [--name=<name>] as a substring match, and [--format=<format>].

add takes <domain> and requires --type=<type>, --name=<name> (use @ or an empty string for the root) and --value=<value>. [--ttl=<ttl>] defaults to 3600 and [--priority=<priority>] defaults to 10 for MX and SRV.

update takes <domain> and a <record_id> from dns list, with the same flags as add.

delete takes <domain> and a <record_id>.

lookup takes <domain> and shows its ID and DNS zone info.

wp captaincore dns list example.com --type=TXT
wp captaincore dns add example.com --type=MX --name="ghl" --value="mxa.mailgun.org" --priority=10
wp captaincore dns delete example.com 64846032

mailgun

rotate regenerates the per-zone SMTP password at Mailgun and stores it on the matching CaptainCore domain. It takes <zone>, and optionally [--deploy] which runs the deploy-mailgun script against a site so it picks up the new credentials. --deploy requires --site=<slug> and --from-name=<name>. [--environment=<env>] defaults to production and appends non-production values as a slug suffix. [--show-password] prints the new SMTP password.

list shows CaptainCore domains and their Mailgun link status. By default it lists only domains linked to a zone. [--unlinked] finds domains with no zone, [--all] shows every domain, [--check] queries Mailgun live for each linked zone’s verification state (slower), and [--format=<format>] accepts table, json, csv or count.

wp captaincore mailgun list --unlinked
wp captaincore mailgun rotate mg.example.com --deploy --site=example --from-name="Example"

Security and audit queues

scan-queue

Generates a prioritized scan queue for Security Finder audits. It queries all production environments, checks audit coverage, and returns the top sites ranked by number of unaudited components. Options: [--limit=<number>] (default 5), [--format=<format>] and [--all].

component-queue

Returns unique component builds that need auditing, deduplicated by content hash. Unlike scan-queue, which is site-centric, this is component-centric, and for each hash it picks one source site to download from. Options: [--limit=<number>] (default 20), [--format=<format>], [--all] and [--model=<id>]. With --model set to a canonical auditor ID such as claude-opus-4-7, hashes audited only by other models are still returned, which supports layered multi-model audits.

update-queue

Builds and caches the “update before audit” queue that the Coverage tab reads. It walks every production environment, resolves each unaudited or out of date plugin and theme to the version it should move to (the wp.org published latest where available, otherwise the newest build already in the fleet), and stores the result in the cc_update_queue transient served by GET /captaincore/v1/update-queue.

This is the heavy job, and its docblock says to run it from system cron:

0 8 * * * /usr/local/bin/wp captaincore update-queue --path=/path/to/site

[--skip-wporg] skips live api.wordpress.org lookups and overlays only cached versions.

core-update-runs

Lists fleet core-update probe and apply runs, or shows one run’s per-site results. Pass [<run_id>] to show a single run, or omit it to list recent runs. Filters are [--result=<result>] accepting ok or fail, [--stage=<stage>] accepting boot, render, http, root, memory and others, [--error-class=<class>], and [--status=<status>] accepting open, triaged, resolved or ignored. [--group] prints error class counts instead of rows.

wp captaincore core-update-runs 12 --result=fail --format=json
wp captaincore core-update-runs 12 --group

session-alerts

Emails a digest of unalerted session and privilege anomalies. It reads session-snapshot rows whose change detection fired at high or critical severity and that have not been alerted, groups them by environment, sends one digest to the admin, and stamps the rows so they are not re-sent. Detection itself happens inline at snapshot ingest.

Options: [--severity=<level>] accepting high or critical (default high), [--to=<email>] overriding the site admin email, and [--dry-run].

Intended to run hourly from system cron.

web-risk-check

Checks all production site URLs against Google’s Web Risk API and emails a notification when threats are detected. Options: [--dry-run] shows what would be checked without calling the API, and [--test] sends a sample email with fake threat data. It requires the GOOGLE_WEB_RISK_API_KEY constant unless you pass --dry-run.

Cron job.

mu-manifest-generate

Reports on mu-plugin manifest coverage across the fleet. It compares each site’s mu_plugin_files data against stored manifests to find unmanifested mu-plugins, manifest drift, orphan files not claimed by any manifest or slug, and version changes. Option: [--format=<format>].

Reporting and housekeeping

scheduled-reports

Sends scheduled maintenance reports. It exists so reports can be triggered manually rather than through WordPress cron, which times out.

send accepts [--dry-run] to show which reports are due without sending, [--all] to send every scheduled report regardless of its next_run time, and [--id=<id>] to send one report.

list prints all scheduled reports.

wp captaincore scheduled-reports send --dry-run

Cron job.

top-plugins

Lists the most used active plugins across production environments. Options: [--limit=<number>] (default 100) and [--format=<format>] accepting table, csv or json.

restic-cache

Lists environments by restic cache size. Options: [--limit=<number>] (default 50) and [--format=<format>].

security-log-sizes

Lists environments by security log size. Options: [--limit=<number>] (default 50) and [--format=<format>].

error-log-sizes

Lists environments by error log size, or shows a per-file breakdown for one site when you pass [<site_id>]. Options: [--limit=<number>] (default 50) and [--format=<format>].

wp captaincore error-log-sizes
wp captaincore error-log-sizes 123

The three size commands are read-only reporting, useful on a schedule when you want to catch a runaway log before it fills a disk.