Remote Scripts
What remote scripts are, how they are piped over SSH, and a catalogue of every script that ships in lib/remote-scripts.
Remote scripts are bash scripts that live on the CaptainCore server and run on a managed WordPress site. They are the layer that does the actual work on a site: collecting data, scanning for malware, applying updates, deploying integrations.
They ship in lib/remote-scripts/ in the captaincore repository and are read from ~/.captaincore/lib/remote-scripts/ at run time.
How they are executed
A remote script is never copied to the site. The local file is redirected into the SSH session’s standard input and read by bash -s:
ssh <options> user@host "cd public/ && bash -s -- --site=<site> <extra flags>" < ~/.captaincore/lib/remote-scripts/<name>
Three consequences follow from that:
- Editing a script takes effect on the very next run. There is nothing to rebuild and nothing to deploy.
- Nothing is written to the customer’s server. The script exists only for the life of the SSH session.
- The script gets the site slug as
--site=<slug>automatically, and any flags you pass through are shell-quoted and appended after it.
You invoke one with captaincore ssh:
captaincore ssh mysite-production --script=detect-seo-spam
captaincore ssh mysite --script=plugin-diff --plugin=woocommerce --stat
captaincore ssh @production --script=find-nulled-components --parallel=20
--script accepts a path first. If the path does not exist on disk, the CLI falls back to ~/.captaincore/lib/remote-scripts/<name>, so you can point at a one-off script anywhere on the server without adding it to the repository.
Most scripts parse their own flags using the shared arguments helper, which turns --foo=bar into $foo and --flag into $flag=true.
To see what is installed with a one-line description parsed from each script’s header:
captaincore script list
Data fetching
These collect information and print it back. They are read-only.
| Script | Description |
|---|---|
arguments |
Arguments helper. Parses CLI flags and arguments, sourced by other scripts rather than run on its own |
archive-logs |
Enumerates rotated access and error log files for archival |
check-security-log-size |
Reports the size of the captaincore_security_log table |
component-hashes |
Generates per-component SHA256 content hashes for plugins, themes and mu-plugins |
fetch-database-tables |
Fetch database tables |
fetch-error-log-size |
Fetches total error log size in bytes |
fetch-folder-size |
Calculate folder size in bytes |
fetch-log-file |
Fetches a server log file |
fetch-log-files |
Fetches list of server logs |
fetch-site-data |
Fetches site data. The comprehensive sync used by captaincore sync-data |
fetch-token |
Fetch token |
fetch-users-logins |
Fetches WordPress user info for security analysis |
file-manager |
List directories and read files, locked to the site’s home directory. Backs the Manager’s Files tab. Supports --action=list|view|delete with a base64 path |
master-db-query |
Export select ids from the CaptainCore CLI master. Runs against the local master data directory, not a customer site |
quicksave-fingerprint |
Generates a content fingerprint for quicksave change detection |
Backups and database
| Script | Description |
|---|---|
db-backup |
Backup database to a private directory |
db-import |
Import database |
db-convert-to-innodb |
Convert MyISAM database tables to InnoDB |
db-code-audit |
Database-stored executable code audit |
migrate |
Migrate a site from a backup snapshot. Takes --url=<backup-url> and an optional --update-urls |
plugins-zip |
Generates plugin zips on the site |
vault |
Manages secure, full site snapshots in a remote Restic repository backed by Backblaze B2. Forked from the CaptainCore do utility |
Security detection
All of these are read-only scanners. Most emit pipe-delimited findings so they can be aggregated across a fleet sweep.
| Script | Description |
|---|---|
detect-binary-payloads |
Detect native binary and reverse-shell payloads in a WordPress web tree |
detect-database-triggers |
Detect WordPress database-layer persistence mechanisms |
detect-elevated-permissions |
Detect WordPress roles and users with elevated permissions they should not have |
detect-empty-homepage |
Detect empty or hollow homepages |
detect-fake-dates |
Detect files with forged timestamps, where mtime is older than birth time |
detect-forged-registrations |
Detect WordPress users with forged user_registered dates |
detect-malformed-passwords |
Detect WordPress users with malformed or legacy password hashes |
detect-sc-timestomp |
Detect the SC / sleek-router-kit toolkit family by timestomp modulus |
detect-seo-cloaking |
Detect Googlebot-cloaking SEO spam, the doorway and full-page-swap variant |
detect-seo-spam |
Detect SEO spam injection in WordPress content |
detect-upload-probes |
Detect attacker upload-probe files |
detect-user-enumeration |
Detect user enumeration leaks on a public WordPress site |
detect-web3-injection |
Hunt for EtherHiding and blockchain-hosted malware loaders, and the service-worker delivery shape used to install them |
find-fake-plugin |
Detect the “updatecore” fake WordPress plugin and variants |
find-nulled-components |
Detect nulled and pirated WordPress plugins and themes |
inventory-compromised-passwords |
Every CaptainCore Helper breached-password event. Emits ACTION|USER_LOGIN|ROLE|IP|TIMESTAMP, where BLOCKED means the login was refused and WARNED means it was allowed and only recorded |
malware-hunt |
WordPress malware hunter, a standalone scanner |
php-in-uploads |
Scans for PHP files and suspicious patterns in wp-content/uploads |
plugin-diff |
Show a unified diff between an installed plugin and a clean copy from wordpress.org. Takes --plugin=<slug> plus optional --version=, --stat and --keep |
report-compromised-passwords |
Reports compromised-password login attempts blocked by CaptainCore Helper |
sals-detector |
Detect WordPress malware families catalogued by Sal Aguilar |
verify-google-analytics-takeover |
Add a verification file for a Google Analytics takeover request |
Maintenance
| Script | Description |
|---|---|
activate |
Activate. Removes the deactivate mu-plugin |
deactivate |
Deactivate. Installs the holding-page mu-plugin |
email-health-check |
Sends an email health check |
launch |
Launches a site. Updates the URL from dev to live, enables search engine indexing and clears cache |
performance-monitor-deploy |
Deploys performance monitor scripts and a */10 * * * * crontab entry |
performance-monitor-remove |
Removes performance monitor scripts and the crontab entry |
reset-permissions |
Reset file and folder permissions to defaults |
restic-cache-check |
Checks for a Restic cache on the remote server |
restic-cache-purge |
Safely removes the Restic cache directory from a remote server |
rewrite-prep |
Preps permalinks if needed |
update |
Update themes and plugins |
update-core |
Sideload a WordPress core build, boot the live site against it with the same wp-config, database and wp-content, probe for PHP fatals, then optionally apply the update. A failed probe aborts before any live core file changes |
update-core is the script behind the fleet core-update sweep. When it is run through captaincore ssh @all --script=update-core, the bulk runner switches to a one-line-per-site mode, prints a summary, stores the run and emails a failure digest. See running commands.
Deployment and setup
| Script | Description |
|---|---|
apply-https |
Apply SSL using the domain without www |
apply-https-with-www |
Apply SSL using the domain with www |
deploy-fathom |
Deploy the Fathom Analytics tracker |
deploy-helper |
Deploy the CaptainCore Helper plugin |
deploy-mailgun |
Deploys Mailgun |
kickstart |
Server setup notes. A Markdown checklist for provisioning a CaptainCore server, not an executable script |
prepare-wordpress |
Prepares a new WordPress site |
Writing your own
A remote script only needs to read its flags and print results. Because it arrives on stdin, it cannot read stdin itself, and because the shell has already changed into the site’s home directory, paths can be relative to the WordPress root.
Two conventions make a script fit the fleet tooling:
- Put a
# Description: <text>line or a leading comment description in the first twenty lines, socaptaincore script listcan describe it. - Emit findings one per line with a stable delimiter, typically
SEVERITY|TYPE|PATH|DETAIL, so results can be aggregated across a@allsweep.
Then run it without adding it to the repository:
captaincore ssh mysite --script=/home/user/scratch/my-check