diff --git a/src/assets/screenshots/whp/whp-backups-history.png b/src/assets/screenshots/whp/whp-backups-history.png
deleted file mode 100644
index 857bc62..0000000
Binary files a/src/assets/screenshots/whp/whp-backups-history.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-backups-settings.png b/src/assets/screenshots/whp/whp-backups-settings.png
deleted file mode 100644
index bc6f43a..0000000
Binary files a/src/assets/screenshots/whp/whp-backups-settings.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-backups.png b/src/assets/screenshots/whp/whp-backups.png
new file mode 100644
index 0000000..4b6baaf
Binary files /dev/null and b/src/assets/screenshots/whp/whp-backups.png differ
diff --git a/src/assets/screenshots/whp/whp-dashboard.png b/src/assets/screenshots/whp/whp-dashboard.png
new file mode 100644
index 0000000..949fcb4
Binary files /dev/null and b/src/assets/screenshots/whp/whp-dashboard.png differ
diff --git a/src/assets/screenshots/whp/whp-domains-add.png b/src/assets/screenshots/whp/whp-domains-add.png
deleted file mode 100644
index 70d7eda..0000000
Binary files a/src/assets/screenshots/whp/whp-domains-add.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-domains.png b/src/assets/screenshots/whp/whp-domains.png
new file mode 100644
index 0000000..745e37d
Binary files /dev/null and b/src/assets/screenshots/whp/whp-domains.png differ
diff --git a/src/assets/screenshots/whp/whp-email-add.png b/src/assets/screenshots/whp/whp-email-add.png
deleted file mode 100644
index ba79071..0000000
Binary files a/src/assets/screenshots/whp/whp-email-add.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-email-archive.png b/src/assets/screenshots/whp/whp-email-archive.png
deleted file mode 100644
index 273f6c0..0000000
Binary files a/src/assets/screenshots/whp/whp-email-archive.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-email.png b/src/assets/screenshots/whp/whp-email.png
new file mode 100644
index 0000000..e8828b9
Binary files /dev/null and b/src/assets/screenshots/whp/whp-email.png differ
diff --git a/src/assets/screenshots/whp/whp-monitor.png b/src/assets/screenshots/whp/whp-monitor.png
deleted file mode 100644
index 25fff4c..0000000
Binary files a/src/assets/screenshots/whp/whp-monitor.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-mysql.png b/src/assets/screenshots/whp/whp-mysql.png
new file mode 100644
index 0000000..7355ffa
Binary files /dev/null and b/src/assets/screenshots/whp/whp-mysql.png differ
diff --git a/src/assets/screenshots/whp/whp-postgres.png b/src/assets/screenshots/whp/whp-postgres.png
new file mode 100644
index 0000000..8916b7a
Binary files /dev/null and b/src/assets/screenshots/whp/whp-postgres.png differ
diff --git a/src/assets/screenshots/whp/whp-resources.png b/src/assets/screenshots/whp/whp-resources.png
deleted file mode 100644
index 1edf68b..0000000
Binary files a/src/assets/screenshots/whp/whp-resources.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-site-monitoring.png b/src/assets/screenshots/whp/whp-site-monitoring.png
new file mode 100644
index 0000000..0d88247
Binary files /dev/null and b/src/assets/screenshots/whp/whp-site-monitoring.png differ
diff --git a/src/assets/screenshots/whp/whp-sites-add.png b/src/assets/screenshots/whp/whp-sites-add.png
deleted file mode 100644
index 6788510..0000000
Binary files a/src/assets/screenshots/whp/whp-sites-add.png and /dev/null differ
diff --git a/src/assets/screenshots/whp/whp-sites.png b/src/assets/screenshots/whp/whp-sites.png
new file mode 100644
index 0000000..c18e30a
Binary files /dev/null and b/src/assets/screenshots/whp/whp-sites.png differ
diff --git a/src/assets/screenshots/whp/whp-valkey.png b/src/assets/screenshots/whp/whp-valkey.png
new file mode 100644
index 0000000..5ac3661
Binary files /dev/null and b/src/assets/screenshots/whp/whp-valkey.png differ
diff --git a/src/content/docs/whp/add-ons/archival-email.mdx b/src/content/docs/whp/add-ons/archival-email.mdx
index b76c32b..0e340e7 100644
--- a/src/content/docs/whp/add-ons/archival-email.mdx
+++ b/src/content/docs/whp/add-ons/archival-email.mdx
@@ -18,6 +18,8 @@ Archival email keeps a long-term, searchable copy of your mail **outside** the l
- Compliance or policy requires you keep email for a fixed window.
- You want a recovery option for mail you accidentally delete from the live mailbox.
+It's powered by our [Bichon](https://anhonesthost.com/bichon/) archival service.
+
## How it's different from backups
| | Backups | Archival email |
@@ -30,22 +32,18 @@ You can use both — they cover different problems.
## What's included
-- **Per-mailbox archive** — enable on the mailboxes that need it, not the whole account.
-- **14-day quick-restore window** — accidentally deleted mail is recoverable without staff help.
-- **Configurable longer retention** — set retention to match your policy.
-- **Independent password reset** on the archive without touching the live mailbox — so you can grant audit access without disturbing the user.
+- **Per-mailbox archive.** Enable on the mailboxes that need it, not the whole account. Your plan has an **archival slots** quota; the Email page shows current usage (e.g. `Archival: 0 of 0 mailboxes archived (no archival slots in your plan)` if you haven't added the add-on yet).
+- **14-day quick-restore window.** Accidentally deleted mail is recoverable without staff help.
+- **Configurable longer retention.** Set retention to match your policy.
+- **Independent password reset on the archive.** Grant audit access without disturbing the live mailbox.
## How to enable
From the [client portal](https://secure.anhonesthost.com/clientarea.php), open **Add-ons** under your hosting service and pick Archival email. Billing is per archived mailbox.
-## How to search archived mail
+## How to use it
-In WHP, go to the sidebar: **Email → Archive**. Pick the mailbox, then search by date range, sender, subject, or body content.
-
-
-
-Click a result to view the message in place; click **Restore to mailbox** to send it back to the live mailbox.
+The archive is managed alongside your regular mailboxes on the **Email** page in WHP — the **Email Accounts** section shows archive status per mailbox, and the per-mailbox menu lets you search and restore archived mail.
## Related
diff --git a/src/content/docs/whp/add-ons/monitoring.mdx b/src/content/docs/whp/add-ons/monitoring.mdx
index c8cdcc5..b689a08 100644
--- a/src/content/docs/whp/add-ons/monitoring.mdx
+++ b/src/content/docs/whp/add-ons/monitoring.mdx
@@ -1,5 +1,5 @@
---
-title: Monitoring (AI Monitor)
+title: Site Monitoring
description: Proactive alerts for site errors, brute-force attempts, and exploit signatures.
sidebar:
order: 2
@@ -12,9 +12,9 @@ import Support from '~/content/partials/support-link.mdx';
## What it does
-AI Monitor watches your site's logs, error rates, and access patterns in the background. If something looks unusual — a sustained spike in 500 errors, a brute-force pattern against a login page, a request matching a known exploit signature — you get an alert.
+Site Monitoring watches your site's logs, error rates, and access patterns in the background. If something looks unusual — a sustained spike in 500 errors, a brute-force pattern against a login page, a request matching a known exploit signature — you get an alert.
-By default alerts go to the contact email on your account; SMS is available for critical-severity events if you opt in.
+By default, alerts go to the contact email on your account. SMS is available for critical-severity events if you opt in.
## What's included
@@ -26,15 +26,17 @@ By default alerts go to the contact email on your account; SMS is available for
## How to enable
-From the [client portal](https://secure.anhonesthost.com/clientarea.php), go to your hosting service → **Upgrade/Downgrade** → pick Monitoring.
+Site Monitoring is enabled per site. From the [client portal](https://secure.anhonesthost.com/clientarea.php), go to your hosting service → **Upgrade/Downgrade** and pick Site Monitoring.
## Where it lives in WHP
-Once enabled, find it in the sidebar: **Security → AI Monitor**.
+Once enabled, find it in the sidebar: **Site Monitoring**.
-
+
-You'll see a feed of events, severity badges, and counts. Click an event for the full log context.
+When the add-on isn't enabled, the page reads: *"AI monitoring is not enabled for any of your sites. Contact your hosting provider to enable this feature."*
+
+Once enabled, the page shows a feed of events with severity badges and counts. Click an event for the full log context.
## Tuning false positives
diff --git a/src/content/docs/whp/add-ons/overview.mdx b/src/content/docs/whp/add-ons/overview.mdx
index ee39ecd..623efc1 100644
--- a/src/content/docs/whp/add-ons/overview.mdx
+++ b/src/content/docs/whp/add-ons/overview.mdx
@@ -16,7 +16,7 @@ Add-ons are **optional extras** you can layer onto your base hosting plan. Each
## What's available
-- **[Monitoring (AI Monitor)](/whp/add-ons/monitoring/)** — proactive alerts when something looks wrong (error spikes, brute-force attempts, exploit signatures).
+- **[Site Monitoring](/whp/add-ons/monitoring/)** — proactive alerts when something looks wrong (error spikes, brute-force attempts, exploit signatures).
- **[Archival email](/whp/add-ons/archival-email/)** — long-term, searchable retention of your mail outside the live mailbox.
- **[Resource upgrades](/whp/add-ons/resource-upgrades/)** — extra CPU, RAM, or storage without changing plans.
- **[Email upgrades](/whp/add-ons/email-upgrades/)** — extra mailboxes or larger per-mailbox storage caps.
diff --git a/src/content/docs/whp/add-ons/resource-upgrades.mdx b/src/content/docs/whp/add-ons/resource-upgrades.mdx
index fec42d6..4d504bb 100644
--- a/src/content/docs/whp/add-ons/resource-upgrades.mdx
+++ b/src/content/docs/whp/add-ons/resource-upgrades.mdx
@@ -18,31 +18,28 @@ Boost your container's **CPU**, **RAM**, or **persistent disk** without migratin
Each resource is independent — upgrade only what you need.
-- **CPU cores** — add cores for sustained-compute workloads.
-- **RAM** — for memory-hungry apps (caches, large WordPress sites with many plugins, Node apps holding big in-memory state).
-- **Persistent disk** — for sites that store a lot of files (media libraries, user uploads, backups outside our managed backup).
+- **CPU cores per container** — visible on the Sites page as **CPU per Container (cores)** (default `0.25`). Add cores for sustained-compute workloads.
+- **RAM per container** — visible on the Sites page as **Memory per Container (MB)** (default `256`). For memory-hungry apps (caches, large WordPress sites with many plugins, Node apps holding big in-memory state).
+- **Number of containers** — scale a busy site horizontally from 1 up to 10 replicas; WHP load-balances traffic across them.
+- **Persistent disk** — for sites that store a lot of files (media libraries, user uploads, etc.).
## When you might need this
-- **High CPU on busy days.** Your **Overview → Resource usage** chart consistently hits the cap during peak hours.
-- **"Out of memory" errors** in your app log or `dmesg`.
-- **Disk usage approaching 80%** of your allocation — get ahead of it; full disks cause backup failures and uploads to fail.
+- **High CPU on busy days.** Sustained-load sites consistently hit the per-container CPU cap during peak hours.
+- **"Out of memory" errors** in your app log.
+- **Disk usage approaching 80%** of your allocation. Get ahead of it — full disks cause backup failures and uploads to fail.
## How to enable
-From the [client portal](https://secure.anhonesthost.com/clientarea.php), open your hosting service → **Upgrade/Downgrade** → pick the resource amount. WHP applies the change usually within a minute, no downtime.
+For per-container CPU/RAM upgrades, edit the values directly on the **Sites** page for the site you want to change, and click **Save**. For account-wide upgrades (more total RAM, more total disk), open the [client portal](https://secure.anhonesthost.com/clientarea.php), go to your hosting service → **Upgrade/Downgrade**, and pick the resource increment.
## See your current usage
-In WHP, sidebar: **Overview → Resource usage**.
-
-
-
-The chart shows CPU, RAM, and disk over the last 24 hours and 7 days. If you're consistently above 80% of any line, that's the one to upgrade.
+Open the **Dashboard** page in WHP. The **Server Information** and account stats show what you're consuming. For per-site usage, open a site from the **Sites** page.
## Related
-- [Monitoring (AI Monitor)](/whp/add-ons/monitoring/) — catches resource saturation before customers complain.
+- [Site Monitoring](/whp/add-ons/monitoring/) — catches resource saturation before customers complain.
- [Add-ons overview](/whp/add-ons/overview/)
## Still stuck?
diff --git a/src/content/docs/whp/how-to/add-a-domain.mdx b/src/content/docs/whp/how-to/add-a-domain.mdx
index cb2ac87..a75db4c 100644
--- a/src/content/docs/whp/how-to/add-a-domain.mdx
+++ b/src/content/docs/whp/how-to/add-a-domain.mdx
@@ -27,35 +27,41 @@ import Support from '~/content/partials/support-link.mdx';
-1. From the sidebar, open **Domains → Add Domain**.
- 
+1. In the sidebar, click **Domains**.
+ 
-2. Enter your domain (for example, `example.com`). Don't include `http://` or `www.` — just the bare domain.
+2. Under **Add New Domain** on the left, type your domain (for example, `example.com`). Don't include `http://` or `www.` — just the bare domain.
-3. Choose the domain type:
- - **Primary** — your account's main domain. Pick this if it's your first.
- - **Add-on** — an extra domain alongside your primary.
-
-4. Click **Add**. WHP confirms the domain and shows the nameserver values and an A record you can point at.
+3. Click **Add Domain**.
+WHP creates the standard set of DNS records automatically for the new domain:
+
+- **A record** for the apex domain → your server's IP
+- **CNAME** for `www` → the apex
+- **NS records** for the nameservers
+- **MX record** → the mail server
+- **TXT record** for SPF
+
+You can review and tweak any of these from the **DNS Management** panel on the right side of the Domains page (select the domain from the dropdown).
+
## Point your DNS at us
There are two paths depending on where the domain is registered:
-**Registered with us:** nothing to do. We set DNS automatically when the domain was created. Skip ahead to verify.
+**Registered with us.** Nothing to do — we set DNS automatically when the domain was added. Skip ahead to verify.
-**Registered elsewhere:** at your registrar, do one of the following:
+**Registered elsewhere.** At your registrar, do one of the following:
-- Set the **nameservers** to `ns1.anhonesthost.com` and `ns2.anhonesthost.com` (recommended — gives us full DNS control, easier to support), **or**
-- Keep your existing nameservers and add an **A record** pointing the domain to the IP address shown on the WHP confirmation screen.
+- Set the **nameservers** to the values shown on the Domains page (recommended — gives us full DNS control, easier to support), **or**
+- Keep your existing nameservers and add an **A record** pointing the domain to the IP shown on the **Dashboard** page under Server Information.
## Verify it worked
-DNS changes can take up to 24 hours to propagate worldwide. To check status:
+DNS changes can take up to 24 hours to propagate worldwide. To check:
-- Run `dig example.com +short` from a terminal — once you see our IP in the result, you're live.
+- Run `dig example.com +short` from a terminal — once you see our server's IP in the result, you're live.
- Or use a web tool like [whatsmydns.net](https://www.whatsmydns.net/) to see propagation across many regions.
Once DNS resolves, visiting your domain in a browser will reach WHP — though you'll see a default placeholder until you create a site.
@@ -64,9 +70,9 @@ Once DNS resolves, visiting your domain in a browser will reach WHP — though y
**"Pending DNS" for more than 24 hours.** Re-check the nameservers or A record at your registrar — typos and trailing dots cause silent failures. Clear your local DNS cache to rule out client-side caching.
-**Domain returns a generic "site not found" page.** DNS works but you haven't created a site yet. [Create one](/whp/how-to/create-a-site/) and point it at this domain.
+**Domain returns a generic placeholder.** DNS works but you haven't created a site yet. [Create one](/whp/how-to/create-a-site/) and bind this domain to it.
-**Wrong site loads.** Another site on your account is set as the default. In **Sites**, make sure the correct site is bound to this domain.
+**Wrong site loads.** Another site on your account is bound to this domain. Open **Sites** in the sidebar and make sure the correct site is bound to it.
## Related
diff --git a/src/content/docs/whp/how-to/backups.mdx b/src/content/docs/whp/how-to/backups.mdx
index d8b36b7..106cd13 100644
--- a/src/content/docs/whp/how-to/backups.mdx
+++ b/src/content/docs/whp/how-to/backups.mdx
@@ -1,6 +1,6 @@
---
title: Backups
-description: Confirm WHP is backing up your site, restore individual files, and download archives.
+description: Run on-demand and scheduled backups of your sites and databases, and confirm they're succeeding.
sidebar:
order: 4
---
@@ -9,76 +9,79 @@ import { Steps, Aside } from '@astrojs/starlight/components';
import SignIn from '~/content/partials/signing-in.mdx';
import Support from '~/content/partials/support-link.mdx';
-WHP runs **automatic daily backups** of your site files and databases. You don't have to set anything up for this to happen — but it's worth knowing how to confirm a backup ran, restore something, and download an archive when you need to.
+WHP keeps automatic backups of your sites and databases. The **Backup Management** page lets you trigger an on-demand backup, add a scheduled backup, manage where backups are sent, and review history.
-## What's backed up by default
+## What's backed up
-- **Site files** — everything under each domain folder.
-- **Databases** — all MySQL databases attached to your account.
-- **Email** — mailboxes and their contents (depending on your plan).
+- **Sites** — files for each site.
+- **Databases** — every MySQL and PostgreSQL database attached to your account.
-Retention is shown on your plan page. The default tier keeps **7 daily backups**.
+Default retention on built-in backup targets is **5 days, up to 10 backups**.
## Sign in to WHP
-## Configure your backup schedule
+## Run an on-demand backup
-1. From the sidebar, open **Backups → Settings**.
- 
+1. In the sidebar, click **Backups**.
+ 
-2. Confirm the **schedule** (default: daily, overnight). Adjust if your plan permits.
+2. Under **Create New Backup**, pick a **Backup Type** (Sites, Databases, or both) and a **Backup Target** from the dropdown.
-3. (Optional) Enable **off-server backups** to a separate storage location — recommended for anything you can't afford to lose.
+3. Click **Start Backup**. The run appears in **Backup History** below; status updates live.
-## Verify a backup actually ran
+## Schedule a backup
-1. Open **Backups → History**.
- 
+1. Scroll down to **Scheduled Backups** and click **+ Add Schedule**.
-2. Confirm the most recent entry is from **within the last 24 hours** and shows **Success**.
+2. Pick the cadence (daily, weekly, etc.), the type (sites / databases / both), and the target.
-3. Click the entry to see what was included — files, databases, email — and the total size.
+3. Save. The schedule appears in the list and runs automatically.
+
+
+
+## Where backups are stored
+
+The **Backup Targets** table shows the destinations available to your account. Built-in targets are S3-backed (for example, `WHP01 S3 Backups`) with retention and a maximum backup count. **Global** targets are managed by us; you can also add your own external target (for example, your own S3 bucket) with **+ Add Backup Target**.
+
+## Verify backups are succeeding
+
+
+
+1. Open **Backups**.
+
+2. Look at the **Total Backups** and **Total Size** tiles at the top. If Total Backups stays at 0 over time, no backups are running — open a [support ticket](https://secure.anhonesthost.com/submitticket.php).
+
+3. Check **Backup History** for recent entries. Each should show **Success** within minutes of its scheduled time.
## Test a restore (recommended quarterly)
-The best time to discover your backup isn't working is *not* when you actually need it. Test surgically:
-
-1. In **History**, click a recent backup → **Restore preview**.
-2. Pick a single file — your site's `index.php`, for example — and restore just that one file.
-3. Confirm it appears correctly. If yes, the restore pipeline works.
+Find the time to do this before you actually need it. Open a recent backup in **Backup History** → preview the contents → restore a single file (your site's `index.php` is a fine target) → confirm it appears. If the surgical restore works, the pipeline works.
Don't do a full restore unless you genuinely need to — it rewrites your live site.
-## Download a backup
-
-To grab a copy off-server:
-
-1. From **History**, click a backup → **Download**.
-2. WHP packages it as `.tar.gz` and offers a one-time download link that's valid for 24 hours.
-
## Troubleshooting
**Backup failed.** Click the failed row to see the error. Common causes:
- Disk full or close to it — check **Overview → Resource usage** and consider a [resource upgrade](/whp/add-ons/resource-upgrades/).
- A very large mailbox slowed the run — consider the [archival email add-on](/whp/add-ons/archival-email/) to relieve mailbox pressure.
-- Transient lock or maintenance window — let it retry overnight before opening a ticket.
+- Transient lock or maintenance window — let it retry on the next schedule before opening a ticket.
-**Restore "succeeded" but my file isn't there.** Check the **path** shown on the backup's detail page — your restore landed where the backup says it would, which may not match your current site layout if you've moved files around.
+**"No targets available" when starting a backup.** Your account has no backup targets attached. Open a ticket; we'll get one wired up.
## Related
diff --git a/src/content/docs/whp/how-to/create-a-site.mdx b/src/content/docs/whp/how-to/create-a-site.mdx
index 5d5e52d..ddebef1 100644
--- a/src/content/docs/whp/how-to/create-a-site.mdx
+++ b/src/content/docs/whp/how-to/create-a-site.mdx
@@ -1,6 +1,6 @@
---
title: Create a site
-description: Spin up a PHP, Node, or static HTML site on a domain you've added to WHP.
+description: Spin up a containerized site on a domain you've added to WHP.
sidebar:
order: 2
---
@@ -10,10 +10,12 @@ import SignIn from '~/content/partials/signing-in.mdx';
import Hostnames from '~/content/partials/service-hostnames.mdx';
import Support from '~/content/partials/support-link.mdx';
+Every site on WHP runs in one or more containers. The Sites page lets you pick a container type (PHP, Node, static HTML, or one of the other options on the dropdown), bind one or more domains, and set how much CPU and RAM each container gets.
+
## Before you start
- A **domain already added** to your account ([Add a domain](/whp/how-to/add-a-domain/) covers that).
-- Decide what kind of site you're building: **PHP**, **Node**, or **static HTML**.
+- Decide what kind of site you're building. Common container types: **PHP** (WordPress, Laravel, most CMSes), **Node** (custom apps), **Static HTML** (prebuilt bundles from Astro, Hugo, Eleventy, etc.).
- About 5 minutes.
## Sign in to WHP
@@ -24,33 +26,33 @@ import Support from '~/content/partials/support-link.mdx';
-1. From the sidebar, open **Sites → Add Site**.
- 
+1. In the sidebar, click **Sites**.
+ 
-2. Pick the **site type**:
- - **PHP** — best for WordPress, Joomla, Laravel, and most off-the-shelf CMSes.
- - **Node** — for custom apps you build and deploy (Express, Next.js in standalone mode, Fastify, etc.).
- - **Static HTML** — for prebuilt site bundles from tools like Astro, Hugo, Eleventy, or Jekyll. No runtime; just files.
+2. Fill in the **Create New Site** form on the right:
+ - **Site Name** — a friendly label for you to recognise the site later.
+ - **Primary Domain or Subdomain** — pick from the dropdown of domains you've already added.
+ - **Container Type** — PHP, Node, static HTML, etc.
+ - **Number of Containers** — `1` is the right answer for most sites. Bump this up (1–10) if you need to scale a busy site horizontally; WHP load-balances traffic across the replicas.
+ - **Additional Domains/Subdomains (Optional)** — bind extra domains to the same site if you want them to serve the same content.
+ - **CPU per Container (cores)** — defaults to `0.25`. Raise this only if you need to.
+ - **Memory per Container (MB)** — defaults to `256`. Raise this if your app needs more RAM.
-3. Pick the **domain**. The dropdown lists domains you've already added.
+3. Scroll down to **SSL/HAProxy Configuration**. Before you enable HTTPS, make sure your domain's DNS already points to our server's IP — SSL certificates can't be issued for domains that aren't pointed correctly. Then check **Enable HTTPS** to have us request a Let's Encrypt certificate.
-4. (PHP only) Pick the **PHP version**.
-
-5. (Node only) Pick the **Node version** and the **start command** (typically `npm run start`).
-
-6. Click **Create site**. WHP provisions the container — usually under a minute. You'll see the new site in the **Sites** list when it's ready.
+4. Click **Create Site**. WHP provisions the container(s) — usually under a minute. The new site shows up in the **Manage Sites** list on the left of the page.
## Where your files go
-When you connect via SFTP, your account home shows one folder per domain. Inside each domain folder, the layout depends on the site type:
+When you connect via SFTP, your account home (`/docker/users//`) shows one folder per domain. Inside each domain folder, the layout depends on the container type:
-| Site type | Layout | Docroot is... |
-| ------------ | ----------------------------------------------------- | ------------------------------------------------------ |
-| PHP | `public_html/`, `logs/`, `crontab` | `public_html/` |
-| Node | `app/`, `logs/` | Whatever your start command serves; we run it from `app/` |
-| Static HTML | files directly (e.g., `index.html`, `assets/`) | The domain folder itself |
+| Container type | Layout inside the domain folder | Docroot is... |
+| -------------- | ------------------------------------------ | -------------------------------------------------------- |
+| PHP | `public_html/`, `logs/`, `crontab` | `public_html/` |
+| Node | `app/`, `logs/` | Whatever your start command serves; we run it from `app/`|
+| Static HTML | files directly (e.g., `index.html`, `assets/`) | The domain folder itself |
Upload your files to the right place and the site picks them up immediately.
@@ -63,16 +65,18 @@ Upload your files to the right place and the site picks them up immediately.
Open your domain in a browser. You should see your site, or the default "no content yet" placeholder if you haven't uploaded files. For Node sites, give it 15–30 seconds after creation for the process to start.
## Troubleshooting
**You see a default "no content" page.** Upload your files via SFTP — for PHP into `public_html/`, for Node into `app/`, for static directly into the domain folder.
-**Node site returns 502.** Check that your start command runs successfully locally, and that it binds to the port shown on the site's WHP detail page (we set it via an env var; see the site's environment in WHP).
+**Node site returns 502.** Check that your start command runs successfully locally, and that it binds to the port WHP exposes via env var to your container.
-**PHP site shows a blank page or 500.** Check your site's error log (sidebar → Logs → pick the site). Common cause: a syntax error in `index.php` or a missing PHP extension — request extensions via a [support ticket](https://secure.anhonesthost.com/submitticket.php) if needed.
+**PHP site shows a blank page or 500.** Check **Sites → your site → Logs** (or the `logs/` folder in SFTP). Common cause: a syntax error in `index.php` or a missing PHP extension — open a [support ticket](https://secure.anhonesthost.com/submitticket.php) if you need a non-default extension enabled.
+
+**SSL won't issue.** The "Important: ensure your domain is pointed to our server" warning means DNS hasn't propagated yet, or it's pointed somewhere else. Re-check with `dig` and retry.
## Related
diff --git a/src/content/docs/whp/how-to/create-an-email-account.mdx b/src/content/docs/whp/how-to/create-an-email-account.mdx
index f4718b9..7b8ef4e 100644
--- a/src/content/docs/whp/how-to/create-an-email-account.mdx
+++ b/src/content/docs/whp/how-to/create-an-email-account.mdx
@@ -11,7 +11,7 @@ import Support from '~/content/partials/support-link.mdx';
## Before you start
-- A **domain added** to your account.
+- A **domain added** to your account ([Add a domain](/whp/how-to/add-a-domain/) covers that).
- Decide what local part you want — the bit before the `@`. For example, `jane` to get `jane@example.com`.
- About 5 minutes.
@@ -23,62 +23,58 @@ import Support from '~/content/partials/support-link.mdx';
-1. From the sidebar, open **Email → Email Accounts → Add Account**.
- 
+1. In the sidebar, click **Email**.
+ 
-2. Pick the **domain** for this mailbox.
+2. Scroll to **Email Accounts** and use the form to create a new account on one of your domains. You'll be asked for the domain, the local part, a password, and an optional mailbox size cap.
-3. Enter the **local part** (the bit before `@`).
+3. Set a **strong password** — at least 12 characters with a mix of upper case, lower case, numbers, and symbols. Email accounts are common attack targets.
-4. Set a **strong password** — at least 12 characters with a mix of upper case, lower case, numbers, and symbols. Email accounts are common attack targets.
-
-5. Choose a **mailbox size cap**. The default is usually fine; you can raise it later or via an [email upgrade add-on](/whp/add-ons/email-upgrades/).
-
-6. Click **Create**.
+4. Click **Create**. The new account appears in the **Email Accounts** list.
## Set up your email client
-Mail server hostnames are per-domain. Find the exact incoming and outgoing hostnames for your domain on the **Dashboard** page in WHP — that page lists the IMAP, POP3, and SMTP host names along with the recommended ports and security settings.
+The exact IMAP, POP3, and SMTP hostnames are listed on the Email page — click **Setup Instructions → View Instructions** under **Mail Server Access** for a step-by-step that includes the right hostnames, ports, and security settings for your server.
-The typical pattern is:
+The typical settings look like this; substitute the hostname shown in the Setup Instructions:
```text
IMAP (incoming)
- Host:
+ Host:
Port: 993
Security: SSL/TLS
Username: full email address (e.g., jane@example.com)
Password: the one you set above
SMTP (outgoing)
- Host:
+ Host:
Port: 587
Security: STARTTLS
Username: full email address
Password: same as IMAP
```
-For per-client walkthroughs (Outlook, Apple Mail, etc.), see the Email clients section — coming soon.
+For per-client walkthroughs (Outlook, Apple Mail, Thunderbird, etc.), see the Email clients section — coming soon.
## Webmail
-The webmail URL for your domain is also listed on the **Dashboard** page in WHP.
+Click **Webmail Access → Open Webmail** on the Email page to sign in to webmail in a new tab.
## Verify it worked
Send yourself a test message from another account (your personal Gmail, for example). It should arrive within a minute or two and be retrievable from both your client and webmail.
## Troubleshooting
-**Webmail isn't reachable.** DNS for the mail subdomain may still be propagating — wait an hour and try again. The exact URL is on the Dashboard page in WHP.
+**Webmail isn't reachable.** DNS for the mail subdomain may still be propagating — wait an hour and try again.
-**Outgoing mail is bouncing or going to spam.** Check the SPF and DKIM records at your registrar. The email account page in WHP shows the exact records you should have.
+**Outgoing mail is bouncing or going to spam.** Check the SPF and DKIM records. The DKIM Records panel on the Email page shows whether DKIM is configured for each of your domains.
**Client can connect on IMAP but not SMTP.** Some ISPs and corporate networks block outgoing port 587. Try sending from a different network to confirm; if the issue is your network, your ISP is the place to ask.
diff --git a/src/content/docs/whp/reference/service-hostnames.mdx b/src/content/docs/whp/reference/service-hostnames.mdx
index bfce3c0..9ebbf4d 100644
--- a/src/content/docs/whp/reference/service-hostnames.mdx
+++ b/src/content/docs/whp/reference/service-hostnames.mdx
@@ -1,6 +1,6 @@
---
title: Service hostnames
-description: Quick reference for connecting to MySQL, PostgreSQL, Memcached, and Redis from inside your container.
+description: Quick reference for connecting to MySQL, PostgreSQL, and Valkey from inside your container.
sidebar:
order: 1
---
@@ -14,24 +14,23 @@ Use these hostnames from inside your WHP container — they resolve to the inter
## Where do I get credentials?
-Each service's credentials live in a different section of the WHP panel:
-
-- **MySQL** → **Databases → MySQL → Users**
-- **PostgreSQL** → **Databases → PostgreSQL** (if enabled on your plan)
-- **Redis** → **Caching → Redis**
-- **Memcached** — no auth required; the service is isolated per container.
+- **MySQL** — sidebar → **MySQL Management**. Create users and databases there; the **phpMyAdmin** link below it opens an SSO'd phpMyAdmin in a new tab.
+- **PostgreSQL** — sidebar → **PostgreSQL**. Same shape as MySQL Management; the **Adminer** link below it opens an SSO'd Adminer.
+- **Valkey** — sidebar → **Valkey Cache**. Click **Enable Valkey**, pick a memory cap and mode, and you're done — no password needed because connections are network-isolated to your account.
## Mail servers
-Mail server hostnames are **per-domain**, not a single shared hostname. Find the exact IMAP / POP3 / SMTP hostnames for each domain on the **Dashboard** page in WHP.
+Mail server hostnames are not per-domain. Your server has one mail server hostname (for example `mail01.cloud-hosting.io`) that handles IMAP, POP3, and SMTP for every domain on that server. Find your exact mail server hostname on the **Dashboard** page in WHP under **Server Information** — that's also where you'll find your server's IP, primary nameservers, and your home directory.
+
+For step-by-step email-client setup, the Email page has a **Setup Instructions** button that lays out the exact host, port, and security settings.
## Connecting from your laptop
-Direct external connections to these internal services are not exposed for security reasons. Use the panel's tools instead:
+Direct external connections to these internal services are not exposed. Use the panel's tools instead:
- **MySQL** — **phpMyAdmin** in the panel for ad-hoc queries.
- **PostgreSQL** — **Adminer** in the panel.
-- **Redis / Memcached** — use the **Caching** section's built-in CLI panel.
+- **Valkey** — there's no first-party browser tool; use a Valkey/Redis client inside your site's **Terminal** (sidebar → **Terminal**).
## Related
diff --git a/src/content/partials/service-hostnames.mdx b/src/content/partials/service-hostnames.mdx
index e92ce80..00ba8e9 100644
--- a/src/content/partials/service-hostnames.mdx
+++ b/src/content/partials/service-hostnames.mdx
@@ -1,12 +1,11 @@
Some applications need to connect to databases or caches. Use these hostnames from inside your site — they only resolve within your hosting container, so they don't work from your laptop.
-| Service | Hostname | Default port | Notes |
-| ----------- | ----------- | ------------ | ------------------------------------------------------ |
-| MySQL | `mysql` | 3306 | Username, password, and database from your WHP panel. |
-| PostgreSQL | `postgres` | 5432 | Available on most plans; ask support if it's missing. |
-| Memcached | `memcache` | 11211 | No auth; isolated per container. |
-| Redis | `redis` | 6379 | Single DB per site; password in panel. |
+| Service | Hostname | Default port | Notes |
+| ----------- | ----------- | ------------ | -------------------------------------------------------------------------------- |
+| MySQL | `mysql` | 3306 | Username, password, and database from the **MySQL Management** page. |
+| PostgreSQL | `postgres` | 5432 | Username, password, and database from the **PostgreSQL** page. |
+| Valkey | `valkey` | 6379 | Redis wire-compatible (phpredis, Predis, ioredis work unchanged). No password — the connection is network-isolated to your account. Enable on the **Valkey Cache** page first. |
-For **email** (IMAP, POP3, SMTP), the mail server hostname is **per-domain** — check the **Dashboard** page in WHP for the exact hostname to use in your email client or app.
+For **email**, the mail server hostname is a per-server value (e.g. `mail01.cloud-hosting.io`) and is shown on the **Dashboard** page under Server Information.
**Connecting from your laptop?** Use the panel's **phpMyAdmin** (MySQL) or **Adminer** (PostgreSQL) for ad-hoc queries — direct external connections to these internal services are not exposed for security reasons.
diff --git a/tools/screenshots/_discovered-nav.json b/tools/screenshots/_discovered-nav.json
new file mode 100644
index 0000000..1c8787a
--- /dev/null
+++ b/tools/screenshots/_discovered-nav.json
@@ -0,0 +1,98 @@
+[
+ {
+ "text": "Logout",
+ "href": "index.php?whp-action=logout"
+ },
+ {
+ "text": "Dashboard",
+ "href": "index.php?page=dashboard"
+ },
+ {
+ "text": "Sites",
+ "href": "index.php?page=sites"
+ },
+ {
+ "text": "Traffic",
+ "href": "index.php?page=site-traffic"
+ },
+ {
+ "text": "Site Builder BETA",
+ "href": "index.php?page=site-builder"
+ },
+ {
+ "text": "Domains",
+ "href": "index.php?page=domains"
+ },
+ {
+ "text": "WordPress",
+ "href": "index.php?page=wordpress"
+ },
+ {
+ "text": "File Manager",
+ "href": "./filemanager/"
+ },
+ {
+ "text": "Terminal",
+ "href": "index.php?page=terminal"
+ },
+ {
+ "text": "cPanel Import",
+ "href": "index.php?page=cpanel-import"
+ },
+ {
+ "text": "MySQL Management",
+ "href": "index.php?page=database-management"
+ },
+ {
+ "text": "phpMyAdmin",
+ "href": "./phpmyadmin-sso.php"
+ },
+ {
+ "text": "PostgreSQL",
+ "href": "index.php?page=postgresql-management"
+ },
+ {
+ "text": "Adminer",
+ "href": "./adminer-sso.php"
+ },
+ {
+ "text": "Valkey Cache",
+ "href": "index.php?page=account-valkey"
+ },
+ {
+ "text": "Email",
+ "href": "index.php?page=email-management"
+ },
+ {
+ "text": "Backups",
+ "href": "index.php?page=backups"
+ },
+ {
+ "text": "Site Monitoring",
+ "href": "index.php?page=ai-monitor-customer"
+ },
+ {
+ "text": "Delegated Users",
+ "href": "index.php?page=delegated-users"
+ },
+ {
+ "text": "Active Sessions",
+ "href": "index.php?page=active-sessions"
+ },
+ {
+ "text": "Cloud Apache Container",
+ "href": "https://repo.anhonesthost.net/cloud-hosting-platform/cloud-apache-container"
+ },
+ {
+ "text": "Cloud Node Container",
+ "href": "https://repo.anhonesthost.net/cloud-hosting-platform/cloud-node-container"
+ },
+ {
+ "text": "Manage Domains",
+ "href": "index.php?page=domains"
+ },
+ {
+ "text": "Manage Databases",
+ "href": "index.php?page=database-management"
+ }
+]
\ No newline at end of file
diff --git a/tools/screenshots/discover.ts b/tools/screenshots/discover.ts
new file mode 100644
index 0000000..248c48d
--- /dev/null
+++ b/tools/screenshots/discover.ts
@@ -0,0 +1,109 @@
+/**
+ * Discovery script — logs in, captures the dashboard, and probes the sidebar
+ * nav to learn the actual URLs of the sections referenced in our docs.
+ *
+ * Not committed for ongoing use; the canonical capture is run.ts.
+ */
+import { chromium, type Page } from 'playwright';
+import { mkdir, writeFile } from 'node:fs/promises';
+import { resolve, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const OUT_DIR = resolve(__dirname, '../../src/assets/screenshots/whp');
+
+function need(name: string): string {
+ const v = process.env[name];
+ if (!v) throw new Error(`missing env: ${name}`);
+ return v;
+}
+
+const BASE = need('WHP_BASE');
+const USER = need('WHP_USER');
+const PASS = need('WHP_PASS');
+
+async function login(page: Page): Promise {
+ console.log(`navigating ${BASE}/login.php`);
+ await page.goto(`${BASE}/login.php`, { waitUntil: 'domcontentloaded' });
+ console.log(`landed at ${page.url()}`);
+
+ await page.fill('input[name="user"]', USER);
+ await page.fill('input[name="password"]', PASS);
+ await page.click('button[type="submit"]');
+ await page.waitForLoadState('networkidle');
+ console.log(`logged in, now at ${page.url()}`);
+}
+
+async function snapshotPage(page: Page, id: string): Promise {
+ await mkdir(OUT_DIR, { recursive: true });
+ const path = resolve(OUT_DIR, `${id}.png`);
+ await page.screenshot({ path, fullPage: false });
+ console.log(`captured ${id} -> ${path}`);
+}
+
+async function listNavLinks(page: Page): Promise<{ text: string; href: string | null }[]> {
+ // Use locator API to collect all anchors with hrefs (no string-eval).
+ const anchors = page.locator('a[href]');
+ const count = await anchors.count();
+ const out: { text: string; href: string | null }[] = [];
+ for (let i = 0; i < count && out.length < 120; i++) {
+ const a = anchors.nth(i);
+ const href = await a.getAttribute('href');
+ if (!href || href.startsWith('#')) continue;
+ const text = ((await a.textContent()) ?? '').trim().slice(0, 60);
+ if (!text) continue;
+ out.push({ text, href });
+ }
+ return out;
+}
+
+async function probe(page: Page, label: string, hrefHint: string): Promise {
+ const link = page.locator(`a[href*="${hrefHint}"]`).first();
+ if ((await link.count()) === 0) {
+ console.log(` NO MATCH for "${label}" (hint=${hrefHint})`);
+ return null;
+ }
+ const href = await link.getAttribute('href');
+ console.log(` ${label}: ${href}`);
+ return href;
+}
+
+async function main(): Promise {
+ const browser = await chromium.launch({ headless: true });
+ const ctx = await browser.newContext({
+ ignoreHTTPSErrors: true,
+ viewport: { width: 1440, height: 900 },
+ });
+ const page = await ctx.newPage();
+
+ try {
+ await login(page);
+ await snapshotPage(page, '_discovery-dashboard');
+
+ const navLinks = await listNavLinks(page);
+ await writeFile(
+ resolve(__dirname, '_discovered-nav.json'),
+ JSON.stringify(navLinks, null, 2),
+ );
+ console.log(`wrote ${navLinks.length} nav links to _discovered-nav.json`);
+
+ for (const [label, hint] of [
+ ['Domains', 'domain'],
+ ['Sites', 'site'],
+ ['Email', 'email'],
+ ['Backups', 'backup'],
+ ['Monitor', 'monitor'],
+ ['Resources', 'resource'],
+ ['Dashboard', 'dashboard'],
+ ] as const) {
+ await probe(page, label, hint);
+ }
+ } finally {
+ await browser.close();
+ }
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/tools/screenshots/peek.ts b/tools/screenshots/peek.ts
new file mode 100644
index 0000000..57a33f3
--- /dev/null
+++ b/tools/screenshots/peek.ts
@@ -0,0 +1,53 @@
+/**
+ * One-shot peek script: log in, navigate to a specific URL, screenshot it.
+ * Usage: PEEK_URL=... npx tsx tools/screenshots/peek.ts
+ */
+import { chromium, type Page } from 'playwright';
+import { mkdir } from 'node:fs/promises';
+import { resolve, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const OUT_DIR = resolve(__dirname, '../../src/assets/screenshots/whp');
+
+function need(name: string): string {
+ const v = process.env[name];
+ if (!v) throw new Error(`missing env: ${name}`);
+ return v;
+}
+
+const BASE = need('WHP_BASE');
+const USER = need('WHP_USER');
+const PASS = need('WHP_PASS');
+const PEEK = need('PEEK_URL');
+const ID = process.env.PEEK_ID ?? '_peek';
+
+async function login(page: Page): Promise {
+ await page.goto(`${BASE}/login.php`, { waitUntil: 'domcontentloaded' });
+ await page.fill('input[name="user"]', USER);
+ await page.fill('input[name="password"]', PASS);
+ await page.click('button[type="submit"]');
+ await page.waitForLoadState('networkidle');
+}
+
+async function main(): Promise {
+ const browser = await chromium.launch({ headless: true });
+ const ctx = await browser.newContext({ ignoreHTTPSErrors: true, viewport: { width: 1440, height: 900 } });
+ const page = await ctx.newPage();
+ try {
+ await login(page);
+ await page.goto(`${BASE}${PEEK}`);
+ await page.waitForLoadState('networkidle');
+ await mkdir(OUT_DIR, { recursive: true });
+ const out = resolve(OUT_DIR, `${ID}.png`);
+ await page.screenshot({ path: out, fullPage: true });
+ console.log(`captured ${PEEK} -> ${out}`);
+ } finally {
+ await browser.close();
+ }
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});
diff --git a/tools/screenshots/run.ts b/tools/screenshots/run.ts
index ce42d0e..f46b3d4 100644
--- a/tools/screenshots/run.ts
+++ b/tools/screenshots/run.ts
@@ -22,13 +22,57 @@ const WHP_USER = envOrDie('WHP_USER');
const WHP_PASS = envOrDie('WHP_PASS');
async function login(page: Page): Promise {
- await page.goto(`${WHP_BASE}/login`);
- await page.fill('input[name="username"]', WHP_USER);
+ await page.goto(`${WHP_BASE}/login.php`);
+ await page.fill('input[name="user"]', WHP_USER);
await page.fill('input[name="password"]', WHP_PASS);
await page.click('button[type="submit"]');
await page.waitForLoadState('networkidle');
}
+/**
+ * Neutralise server-identifying and account-specific text/labels before the
+ * screenshot — we run a multi-server fleet, so docs shouldn't bake in a single
+ * host's name or IP.
+ *
+ * Runs in the page context. Self-contained: no external bindings, no eval.
+ */
+async function redactSensitive(page: Page): Promise {
+ await page.addStyleTag({
+ content: `
+ .navbar-text { visibility: hidden !important; }
+ .brand-full { visibility: hidden !important; }
+ `,
+ });
+
+ // String swaps for inline text that mask-by-selector can't cover.
+ await page.evaluate(() => {
+ const root = document.body;
+ const swaps: [RegExp, string][] = [
+ // server / mail / nameserver hostnames (with possible -s3 etc. suffix)
+ [/whp\d+(-[a-z0-9]+)?\.cloud-hosting\.io/gi, '.cloud-hosting.io'],
+ [/mail\d+\.cloud-hosting\.io/gi, '.cloud-hosting.io'],
+ [/ns[12]\.whp\d+\.cloud-hosting\.io/gi, 'ns.your-server.cloud-hosting.io'],
+ // backup target / bucket names that bake in server number
+ [/whp\d+(-[a-z0-9]+)?\b/gi, ''],
+ [/WHP\d+(-[A-Z0-9]+)?\b/g, ''],
+ // IP addresses
+ [/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, ''],
+ // home dir + demo user
+ [/\/docker\/users\/[a-z0-9-]+/g, '/docker/users/'],
+ [/demo-user/g, 'your-username'],
+ ];
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
+ const nodes: Text[] = [];
+ let n: Node | null = walker.nextNode();
+ while (n) { nodes.push(n as Text); n = walker.nextNode(); }
+ for (const node of nodes) {
+ let v = node.nodeValue ?? '';
+ for (const [re, replacement] of swaps) v = v.replace(re, replacement);
+ if (v !== node.nodeValue) node.nodeValue = v;
+ }
+ });
+}
+
async function captureShot(page: Page, shot: Shot): Promise {
const viewport = shot.viewport ?? { width: 1440, height: 900 };
await page.setViewportSize(viewport);
@@ -36,6 +80,7 @@ async function captureShot(page: Page, shot: Shot): Promise {
await page.goto(`${WHP_BASE}${shot.path}`);
await page.waitForLoadState('networkidle');
if (shot.waitFor) await page.waitForSelector(shot.waitFor);
+ await redactSensitive(page);
const maskSelectors = [...DEFAULT_MASK, ...(shot.mask ?? [])];
const maskLocators: Locator[] = maskSelectors.map((sel) => page.locator(sel));
diff --git a/tools/screenshots/shots.config.ts b/tools/screenshots/shots.config.ts
index fae6565..e584176 100644
--- a/tools/screenshots/shots.config.ts
+++ b/tools/screenshots/shots.config.ts
@@ -15,19 +15,18 @@ export type Shot = {
/** Always-applied redactions. Per-shot mask is added on top of this list. */
export const DEFAULT_MASK: string[] = [
- '[data-test="account-id"]',
- '[data-test="server-hostname"]',
- '[data-test="user-ip"]',
- '.billing-column',
+ // Conservative defaults — the actual selectors may differ; verify by inspecting
+ // a captured shot and adding more selectors here if anything leaks.
];
export const shots: Shot[] = [
- { id: 'whp-domains-add', path: '/domains/add' },
- { id: 'whp-sites-add', path: '/sites/add' },
- { id: 'whp-email-add', path: '/email/add' },
- { id: 'whp-backups-settings', path: '/backups/settings' },
- { id: 'whp-backups-history', path: '/backups/history' },
- { id: 'whp-monitor', path: '/security/monitor' },
- { id: 'whp-email-archive', path: '/email/archive' },
- { id: 'whp-resources', path: '/overview/resources' },
+ { id: 'whp-dashboard', path: '/index.php?page=dashboard' },
+ { id: 'whp-domains', path: '/index.php?page=domains' },
+ { id: 'whp-sites', path: '/index.php?page=sites' },
+ { id: 'whp-email', path: '/index.php?page=email-management' },
+ { id: 'whp-backups', path: '/index.php?page=backups' },
+ { id: 'whp-site-monitoring', path: '/index.php?page=ai-monitor-customer' },
+ { id: 'whp-mysql', path: '/index.php?page=database-management' },
+ { id: 'whp-postgres', path: '/index.php?page=postgresql-management' },
+ { id: 'whp-valkey', path: '/index.php?page=account-valkey' },
];