Repository navigation
Operations
This page covers deployment, packaging, cache expectations, retention, multisite behavior, backup and the signals worth monitoring. Sources: readme.txt, docs/en/DEPLOYMENT.md, docs/en/operations.md, docs/en/operations/backup-restore.md and bin/*.sh.
The product is a single WordPress plugin with no vendor service, no license server and no telemetry. Keys, audit rows, settings and the pepper stay in the site's own database (readme.txt).
Outbound traffic has exactly one product path: upload-media-from-url, and only when an administrator enables public HTTPS media downloads (off by default) and a key calls it. It never fires on installation, activation, cron or an admin page load. The destination is the client-supplied URL, not a fixed provider (readme.txt, "External services").
The plugin implements no update check of its own: a source search finds no update hooks. Any update check is WordPress core's normal behavior for a plugin distributed through WordPress.org, and this repository neither contains nor evidences that request.
| Deployment fact | Value |
|---|---|
| Minimum PHP / WordPress | 8.2 / 6.9 (xnetvn-agent-hub-wp.php) |
| Required companion | Official MCP Adapter >=0.7.0 <0.8.0, active (src/Bootstrap/Requirements.php) |
| Activation without adapter | Allowed; MCP stays disabled with an admin notice |
| Unsupported adapter | Plugin stays loadable, MCP boot closed |
composer build runs packaging regressions, then bin/build-zip.sh. The builder copies an allowlist into a staging directory, installs production-only Composer dependencies there, removes the lock file from the staging copy only, archives as dist/xnetvn-agent-hub-wp-<version>.zip, and verifies the result.
| Included | Excluded (examples) |
|---|---|
xnetvn-agent-hub-wp.php, readme.txt, LICENSE, uninstall.php, index.php
|
tests/, docs/, bin/, infra/, node_modules/
|
src/, languages/, assets/
|
composer.lock, package.json, package-lock.json
|
composer.json and production vendor/
|
phpunit*, phpcs*, phpstan*, compose*.yaml, Markdown files, dotfiles, lab overrides, .distignore
|
bin/verify-zip.sh requires vendor/autoload.php, src/Bootstrap/Loader.php, assets/admin.js and languages/xnetvn-agent-hub-wp.pot, rejects excluded paths and refuses archive traversal. To check the actual package, infra/remote/check-package.sh activates it in separate CLI-only tables inside the lab and runs Plugin Check (docs/en/development.md).
Do not ship a ZIP built from a dirty tree, and do not treat a structural pass as activation evidence.
Rate limiting uses WpBucketStore:
| Backend | Mechanism | Requirement |
|---|---|---|
| Persistent object cache present |
wp_cache_add / wp_cache_incr in group xnetvn_agent_hub_wp_rate, 180 s TTL |
Shared, atomic increments across PHP workers |
| No persistent cache | Atomic SQL increment in non-autoloaded per-site options | One authoritative database primary |
Operational rules (docs/en/operations.md, docs/en/DEPLOYMENT.md):
- Use
noevictionand a unique cache prefix per site. - Do not enable a provider's "graceful" local-memory fallback: it can lose shared counters.
- A total cache failure can abort WordPress before this plugin loads, producing a core 500 rather than the plugin's 503 unavailable response. Recover the backend first.
- Flushing the cache removes ephemeral buckets, so infrastructure operations must stay outside delegated tools that could reset their own limits.
- Key and audit writes, and MySQL named locks, need one primary for read-your-writes. Multi-primary and NDB clusters are unsupported unless session locking is guaranteed.
Audit rows are buffered and written as one bounded INSERT at shutdown. Retention defaults to 90 days (audit_retention_days) and runs daily on xnetvn_agent_hub_wp_purge_audit, deleting at most 1000 rows per pass and rescheduling a backlog event when more remain. The same pass removes expired rate buckets and notice markers; a storage failure dispatches xnetvn_agent_hub_wp_audit_unavailable instead of reporting a zero backlog (src/Audit/Retention.php).
Settings, tables, pepper and rate buckets are per site. Identical key IDs and peppers on different sites stay independent because every table and option is prefixed per blog (docs/en/architecture/persistence.md).
-
Network-wide deactivation visits every site in bounded pages under
switch_to_blog(), clears the plugin's four owned events, and restores the current blog infinally. Keys, audit rows and settings remain. -
Uninstall checks each site's
delete_data_on_uninstall; a site that preserves data keeps the shared per-user preferences alive (uninstall.php). -
Super administrators keep default use and management, but an explicit site role matrix and corrupt policy still deny delegated use (
docs/en/architecture/overview.md). - New sites get only their own tables; nothing is copied from another blog.
Back up these four stores together; a partial restore breaks authentication:
| Store | Why |
|---|---|
{prefix}xnetvn_agent_hub_wp_keys |
Key metadata and HMAC hashes |
{prefix}xnetvn_agent_hub_wp_audit |
Activity history |
xnetvn_agent_hub_wp_pepper |
Critical. Losing it invalidates every existing key |
xnetvn_agent_hub_wp_settings, xnetvn_agent_hub_wp_db_version
|
Site policy and migration state |
Rollback: enable maintenance mode, restore the previous plugin directory, run wp cache flush, then disable maintenance mode (docs/en/DEPLOYMENT.md). Deleting the pepper option and reloading generates a fresh pepper and immediately invalidates all keys — the correct emergency move after a database leak, never a routine operation (docs/en/guides/operation-guide.md).
The plugin exposes hooks rather than a metrics endpoint. Watch these:
| Signal | Source | Meaning |
|---|---|---|
auth_failed rows and xnetvn_agent_hub_wp_auth_failed
|
Audit table, hook | Missing, malformed, revoked, expired or unknown keys; carries a fixed reason and validated peer IP only |
rate_limited rows, HTTP 429 with Retry-After
|
Audit table, src/Auth/Authenticator.php
|
Per-IP failed-auth or per-key budget exhausted |
denied rows |
Audit table | Policy or capability refusal at call time |
HTTP 401 with WWW-Authenticate
|
REST response headers | Credential missing or rejected |
xnetvn_agent_hub_wp_audit_unavailable |
Hook | Audit storage failing; treat as a data-loss risk |
xnetvn_agent_hub_wp_rotation_incomplete |
Hook | Key rotation did not commit cleanly; reconcile before retrying |
| Audit table growth | Database | Retention cron not completing |
docs/en/guides/monitoring-guide.md publishes indicative thresholds (for example 401 below 1%, 429 below 0.1%, audit table below one million rows). Those are guidance, not measured capacity; do not read them as a service-level guarantee. Confirm the cron event exists with wp cron event list and keep DISABLE_WP_CRON in mind before relying on retention.
Uninstall and deactivation are different operations: deactivation keeps all data; uninstall deletes owned tables and prefixed options by default. Test uninstall only on disposable sites (docs/en/development.md). See Release-and-Versioning for how a package is produced and Testing for how it is checked.
xNetVN Agent Hub WP · GPL-2.0-or-later · Copyright (c) 2026 xNetVN Inc.
This wiki summarizes the repository documentation. The source of truth is the repository; when they disagree, the repository wins.
Start here
Using the plugin
Security
Engineering
Project