Build a production-ready WordPress plugin that:
- Syncs plugin inventory from WordPress to Stack2.
- Receives signed commands from Stack2 and executes allowed plugin actions.
- Returns command results and optional refreshed inventory.
- Uses HMAC-based request signing with timestamp replay protection.
Admin enters these into plugin settings:
- Stack2 Base URL (example: https://app.stack2.au)
- Site ID (issued by Stack2)
- API Key (issued once/rotated by Stack2)
Do not generate these values in WordPress. They come from Stack2.
Endpoint:
- POST {stack2_base_url}/api/websites/plugin-inventory
Required headers:
- X-Stack2-Site-ID: {site_id}
- X-Stack2-Timestamp: unix seconds
- X-Stack2-Signature: hex hmac sha256
Signing message:
- Preferred: POST:stack2-push:{timestamp}:{sha256_hex_of_raw_json_body}
- Compatibility fallback exists server-side, but plugin should use preferred format above.
Body JSON:
{
"site_id": "site_xxx",
"site_url": "https://example.com",
"wp_version": "6.8",
"php_version": "8.3.7",
"collected_at": "2026-05-06T10:10:10Z",
"plugins": [
{
"slug": "seo-by-rank-math",
"file": "seo-by-rank-math/rank-math.php",
"name": "Rank Math SEO",
"version": "1.0.220",
"author": "Rank Math",
"plugin_uri": "https://rankmath.com/",
"description": "SEO plugin",
"is_active": true,
"has_update": false,
"latest_version": null
}
]
}Important:
- Do not send has_vulnerability. It is not part of the payload.
Expose REST route:
- POST /wp-json/stack2/v1/command
Verify headers:
- X-Stack2-Site-ID
- X-Stack2-Timestamp
- X-Stack2-Signature
Signing message to verify:
- POST:/stack2/v1/command:{timestamp}:{sha256_hex_of_raw_json_body}
Timestamp validation:
- Reject if absolute skew > 300 seconds.
Request JSON:
{
"action": "update",
"plugin": "seo-by-rank-math/rank-math.php",
"slug": "seo-by-rank-math"
}Allowed actions:
- install
- update
- activate
- deactivate
- delete
- inventory
Response JSON:
{
"success": true,
"error": null,
"inventory": {
"site_id": "site_xxx",
"site_url": "https://example.com",
"wp_version": "6.8",
"php_version": "8.3.7",
"collected_at": "2026-05-06T10:11:11Z",
"plugins": []
}
}Notes:
- inventory is optional but should be returned for inventory action and after successful mutating actions when practical.
- If HTTP status is non-2xx, still return structured JSON where possible.
- Main plugin bootstrap file.
- Settings/admin page class.
- Inventory collector service.
- Signature/HMAC utility service.
- REST controller for command endpoint.
- Command executor service.
- HTTP client service for push.
- Logger/debug diagnostics helper.
Store in wp_options:
- stack2_base_url
- stack2_site_id
- stack2_api_key
- stack2_auto_sync_enabled
- stack2_sync_interval_minutes
- stack2_last_sync_at
- stack2_last_sync_status
- stack2_last_sync_error
Security:
- Sanitize and validate all fields.
- Restrict settings page to manage_options.
- Mask API key in UI after save (show partial only).
- Register WP-Cron event for periodic inventory sync.
- Trigger immediate sync on:
- plugin activation
- settings saved (if credentials valid)
- Debounce to avoid duplicate concurrent sync runs.
- Use get_plugins() to list installed plugins.
- Use is_plugin_active() for active state.
- Detect available update from update_plugins transient.
- Compute:
- slug from plugin file path first segment
- file as plugin basename
- latest_version if update exists
- Include site_url, wp_version, php_version, collected_at.
- install:
- require slug
- install via Plugin_Upgrader (with FS credentials handling)
- update:
- require plugin file or resolve via slug
- activate/deactivate/delete:
- require plugin file (or resolve slug to file)
- inventory:
- no mutation, return inventory
- Always return structured success/error.
- Wrap mutation operations with robust exception handling and rollback-safe behavior where applicable.
- hash = sha256(raw_body) as lowercase hex.
- message format exactly as contract.
- signature = lowercase hex hmac_sha256(message, api_key).
- Use constant-time compare for signature check.
- Reject missing headers, stale timestamp, invalid signature, wrong site id.
- Inventory push retries:
- exponential backoff (example: 5s, 30s, 2m) for transient 5xx/network errors.
- No retries for 401/403 until credentials changed.
- Persist last error summary for admin UI.
- Add debug mode toggle for detailed logs via error_log with prefix STACK2_PLUGIN.
- Support modern WordPress versions currently in active support.
- Support PHP 8.1+.
- Avoid fatal behavior if WP-CLI unavailable.
- Graceful degradation when filesystem write credentials are required.
- Idempotent inventory pushes.
- Strict input validation for all REST payload fields.
- Minimal blocking work in request cycle.
- No secrets logged in plaintext.
- Unit-testable service boundaries.
- Valid signed inventory push returns 200 from Stack2.
- Invalid signature returns 401.
- Stale timestamp (>300s) rejected.
- Command endpoint rejects bad signature/site id.
- Each action path:
- install
- update
- activate
- deactivate
- delete
- inventory
- Response JSON shape matches contract.
- Update detection maps correctly to has_update/latest_version.
- No has_vulnerability field emitted.
- Cron sync executes and records status.
- Plugin survives missing/invalid settings without fatal errors.
- Plugin can be configured using Stack2 Site ID + API key.
- Manual Sync Now works.
- Scheduled sync works.
- Stack2 commands can mutate plugin state remotely.
- HMAC verification passes in both directions.
- Admin UI shows last sync status/time and safe error messages.
- README includes setup, signing contract, and troubleshooting.
Build a WordPress plugin named Stack2 Connector that integrates with Stack2 API. Implement:
- settings page with Stack2 Base URL, Site ID, API key, sync toggles;
- inventory push to POST /api/websites/plugin-inventory with headers X-Stack2-Site-ID, X-Stack2-Timestamp, X-Stack2-Signature and signing message POST:stack2-push:{timestamp}:{sha256(body)};
- command receiver at POST /wp-json/stack2/v1/command verifying signature message POST:/stack2/v1/command:{timestamp}:{sha256(body)};
- allowed actions install/update/activate/deactivate/delete/inventory;
- structured response {success,error,inventory};
- plugin inventory fields slug,file,name,version,author,plugin_uri,description,is_active,has_update,latest_version (no has_vulnerability);
- WP-Cron periodic sync, sync-now button, retries for transient failures, secure logging and input validation;
- robust service-oriented architecture with testable classes and a README including setup + troubleshooting.
Build a production-ready WordPress plugin that:
- Syncs plugin inventory from WordPress to Stack2.
- Receives signed commands from Stack2 and executes allowed plugin actions.
- Returns command results and optional refreshed inventory.
- Uses HMAC-based request signing with timestamp replay protection.
Admin enters these into plugin settings:
- Stack2 Base URL (example: https://app.stack2.au)
- Site ID (issued by Stack2)
- API Key (issued once/rotated by Stack2)
Do not generate these values in WordPress. They come from Stack2.
Endpoint:
- POST {stack2_base_url}/api/websites/plugin-inventory
Required headers:
- X-Stack2-Site-ID: {site_id}
- X-Stack2-Timestamp: unix seconds
- X-Stack2-Signature: hex hmac sha256
Signing message:
- Preferred: POST:stack2-push:{timestamp}:{sha256_hex_of_raw_json_body}
- Compatibility fallback exists server-side, but plugin should use preferred format above.
Body JSON:
{
"site_id": "site_xxx",
"site_url": "https://example.com",
"wp_version": "6.8",
"php_version": "8.3.7",
"collected_at": "2026-05-06T10:10:10Z",
"plugins": [
{
"slug": "seo-by-rank-math",
"file": "seo-by-rank-math/rank-math.php",
"name": "Rank Math SEO",
"version": "1.0.220",
"author": "Rank Math",
"plugin_uri": "https://rankmath.com/",
"description": "SEO plugin",
"is_active": true,
"has_update": false,
"latest_version": null
}
]
}Important:
- Do not send has_vulnerability. It is not part of the payload.
Expose REST route:
- POST /wp-json/stack2/v1/command
Verify headers:
- X-Stack2-Site-ID
- X-Stack2-Timestamp
- X-Stack2-Signature
Signing message to verify:
- POST:/stack2/v1/command:{timestamp}:{sha256_hex_of_raw_json_body}
Timestamp validation:
- Reject if absolute skew greater than 300 seconds.
Request JSON:
{
"action": "update",
"plugin": "seo-by-rank-math/rank-math.php",
"slug": "seo-by-rank-math"
}Allowed actions:
- install
- update
- activate
- deactivate
- delete
- inventory
Response JSON:
{
"success": true,
"error": null,
"inventory": {
"site_id": "site_xxx",
"site_url": "https://example.com",
"wp_version": "6.8",
"php_version": "8.3.7",
"collected_at": "2026-05-06T10:11:11Z",
"plugins": []
}
}Notes:
- inventory is optional but should be returned for inventory action and after successful mutating actions when practical.
- If HTTP status is non-2xx, still return structured JSON where possible.
- Main plugin bootstrap file.
- Settings/admin page class.
- Inventory collector service.
- Signature/HMAC utility service.
- REST controller for command endpoint.
- Command executor service.
- HTTP client service for push.
- Logger/debug diagnostics helper.
Store in wp_options:
- stack2_base_url
- stack2_site_id
- stack2_api_key
- stack2_auto_sync_enabled
- stack2_sync_interval_minutes
- stack2_last_sync_at
- stack2_last_sync_status
- stack2_last_sync_error
Security:
- Sanitize and validate all fields.
- Restrict settings page to manage_options.
- Mask API key in UI after save (show partial only).
- Register WP-Cron event for periodic inventory sync.
- Trigger immediate sync on plugin activation.
- Trigger immediate sync when settings are saved and credentials are valid.
- Debounce to avoid duplicate concurrent sync runs.
- Use get_plugins() to list installed plugins.
- Use is_plugin_active() for active state.
- Detect available update from update_plugins transient.
- Compute slug from plugin file path first segment.
- Set file as plugin basename.
- Populate latest_version if update exists.
- Include site_url, wp_version, php_version, collected_at.
- install:
- require slug
- install via Plugin_Upgrader (with FS credentials handling)
- update:
- require plugin file or resolve via slug
- activate/deactivate/delete:
- require plugin file (or resolve slug to file)
- inventory:
- no mutation, return inventory
- Always return structured success/error.
- Wrap mutation operations with robust exception handling and rollback-safe behavior where applicable.
- hash = sha256(raw_body) as lowercase hex.
- message format exactly as contract.
- signature = lowercase hex hmac_sha256(message, api_key).
- Use constant-time compare for signature check.
- Reject missing headers, stale timestamp, invalid signature, wrong site id.
- Inventory push retries:
- exponential backoff (for example: 5s, 30s, 2m) for transient 5xx/network errors.
- No retries for 401/403 until credentials changed.
- Persist last error summary for admin UI.
- Add debug mode toggle for detailed logs via error_log with prefix STACK2_PLUGIN.
- Support modern WordPress versions currently in active support.
- Support PHP 8.1+.
- Avoid fatal behavior if WP-CLI unavailable.
- Graceful degradation when filesystem write credentials are required.
- Idempotent inventory pushes.
- Strict input validation for all REST payload fields.
- Minimal blocking work in request cycle.
- No secrets logged in plaintext.
- Unit-testable service boundaries.
- Valid signed inventory push returns 200 from Stack2.
- Invalid signature returns 401.
- Stale timestamp greater than 300s is rejected.
- Command endpoint rejects bad signature/site id.
- Each action path is validated:
- install
- update
- activate
- deactivate
- delete
- inventory
- Response JSON shape matches contract.
- Update detection maps correctly to has_update/latest_version.
- No has_vulnerability field is emitted.
- Cron sync executes and records status.
- Plugin survives missing/invalid settings without fatal errors.
- Plugin can be configured using Stack2 Site ID and API key.
- Manual Sync Now works.
- Scheduled sync works.
- Stack2 commands can mutate plugin state remotely.
- HMAC verification passes in both directions.
- Admin UI shows last sync status/time and safe error messages.
- README includes setup, signing contract, and troubleshooting.
Build a WordPress plugin named Stack2 Connector that integrates with Stack2 API. Implement:
- Settings page with Stack2 Base URL, Site ID, API key, and sync toggles.
- Inventory push to POST /api/websites/plugin-inventory with headers X-Stack2-Site-ID, X-Stack2-Timestamp, X-Stack2-Signature and signing message POST:stack2-push:{timestamp}:{sha256(body)}.
- Command receiver at POST /wp-json/stack2/v1/command verifying signature message POST:/stack2/v1/command:{timestamp}:{sha256(body)}.
- Allowed actions install/update/activate/deactivate/delete/inventory.
- Structured response {success,error,inventory}.
- Plugin inventory fields slug,file,name,version,author,plugin_uri,description,is_active,has_update,latest_version (no has_vulnerability).
- WP-Cron periodic sync, sync-now button, retries for transient failures, secure logging and input validation.
- Robust service-oriented architecture with testable classes and a README including setup and troubleshooting.