# Support Provisioning Portal

Internal WordPress plugin for support staff to provision standardized Proxmox VE VMs and LXC containers without direct Proxmox access.

The application runs as a WordPress plugin and exposes both:

- an admin page at **Support Provisioning**
- a frontend shortcode: `[support_provisioning_portal]`

## Implemented First Slice

- WordPress plugin bootstrap with activation hook
- Custom database tables:
  - `wp_spp_templates`
  - `wp_spp_deployments`
  - `wp_spp_deployment_shares`
  - `wp_spp_audit_logs`
- Seed data for 3 approved templates
- WordPress REST API endpoints:
  - `GET /wp-json/support-provisioning/v1/templates`
  - `GET /wp-json/support-provisioning/v1/quota`
  - `GET /wp-json/support-provisioning/v1/deployments`
  - `GET /wp-json/support-provisioning/v1/deployments/:id`
  - `POST /wp-json/support-provisioning/v1/deployments`
  - `POST /wp-json/support-provisioning/v1/deployments/:id/start`
  - `POST /wp-json/support-provisioning/v1/deployments/:id/stop`
  - `POST /wp-json/support-provisioning/v1/deployments/:id/prolong`
  - `POST /wp-json/support-provisioning/v1/deployments/:id/refresh-ips`
  - `GET /wp-json/support-provisioning/v1/deployments/:id/shares`
  - `POST /wp-json/support-provisioning/v1/deployments/:id/shares`
  - `DELETE /wp-json/support-provisioning/v1/deployments/:id/shares/:user_id`
  - `DELETE /wp-json/support-provisioning/v1/deployments/:id`
- Mock Proxmox adapter for local/no-cluster use
- HTTP token-auth Proxmox adapter behind the same interface
- Audit log rows for every mutating action
- Optional `Never expire` deployments
- Per-user and global RAM contingents
- IP address display for deployments when available from the mock adapter, Proxmox guest agent, or LXC interfaces
- Manual IP refresh action for deployments
- Non-destructive expiration: expired VMs and containers are stopped and locked, not deleted
- Plugin-owned per-user rights for portal access, lifecycle actions, settings, and user-rights management
- Admin-panel user rights management for WordPress and SSO-created users
- Admin-panel template management for importing QEMU VM and LXC templates from the configured Proxmox node
- Owner/private deployment visibility with explicit per-deployment sharing
- Minimal admin/frontend UI for:
  - deployment dashboard
  - templates
  - create deployment form
  - deployment detail view
  - start/stop/delete actions
  - TTL prolongation for expired or active deployments
  - theme-inherited form controls and colors for shortcode rendering

## Install

1. Copy the `support-provisioning-portal` folder into:

   ```text
   wp-content/plugins/support-provisioning-portal
   ```

2. Activate **Support Provisioning Portal** in WordPress admin.

3. Open **Support Provisioning** in the WordPress admin menu.

4. Optional: add this shortcode to an internal page:

   ```text
   [support_provisioning_portal]
   ```

## Permissions

- The plugin uses its own per-user rights stored in user metadata, not WordPress roles.
- SSO users from external identity providers can be granted access after their first WordPress sign-in creates or maps their WordPress user.
- Available plugin rights:
  - Open portal and view deployments
  - Create deployments
  - Start deployments
  - Stop deployments
  - Prolong deployments
  - Refresh IP addresses
  - Delete deployments
  - View and manage all deployments
  - Manage templates
  - Manage Proxmox settings and quotas
  - Manage user rights
- Until the first user is granted **Manage user rights**, WordPress users with `manage_options` have bootstrap access so the first plugin rights manager can be assigned.
- After that bootstrap, portal and lifecycle access are controlled by the plugin rights on each user.
- WordPress REST nonces are required for UI mutations.

## Deployment Visibility And Sharing

Deployments are private by default:

- Regular portal users see deployments they created.
- Users can also see deployments explicitly shared with them.
- Users with **View and manage all deployments** can see and operate on all deployments.

The deployment owner, or a user with **View and manage all deployments**, can share a deployment from its detail view by entering another user's WordPress login or email address. The target user must already have **Open portal and view deployments**.

Shared users can use lifecycle actions only when they also have the matching plugin right, such as **Start deployments** or **Stop deployments**. Shared users cannot delete someone else's deployment unless they also have **View and manage all deployments**.

## Expiration And Contingents

Deployment creation supports either a TTL in hours or **Never expire**.

When a deployment reaches its TTL:

- WordPress cron and REST requests detect the expiration.
- The plugin attempts to stop the VM or container.
- The deployment status becomes `EXPIRED`.
- The deployment cannot be started again until the user prolongs its TTL.
- The user can either prolong the TTL or delete the deployment.
- Deleted deployments are hidden from the active deployment list, but audit rows remain.

RAM contingents are configured from the **Proxmox Settings** section on the **Support Provisioning** admin page:

- Default per-user RAM limit (MB)
- Global RAM limit (MB)

Set either value to `0` for unlimited. Active allocations include deployments in `PROVISIONING`, `STOPPED`, `RUNNING`, and `DELETING` states.

Per-user overrides are available in the **User Rights** section on the **Support Provisioning** admin page. Leave the override empty to use the default per-user limit.

## IP Addresses

Deployments include an `ipAddresses` field in REST responses and show those addresses in the UI. In mock mode, deterministic documentation-range IPs are assigned. In HTTP mode, the plugin reads VM IPs from the Proxmox guest-agent `network-get-interfaces` endpoint when available and reads LXC IPs from the container interfaces endpoint.

If IPs are reported only after boot, use **Refresh IPs** in the deployment detail view.

## Proxmox Settings

The plugin defaults to mock mode. Configure live Proxmox access from the **Support Provisioning** admin page:

- Mode: `Mock` or `HTTP token auth`
- Base URL, for example `https://proxmox.example.internal:8006`. HTTP mode requires an HTTPS URL.
- Token ID, for example `user@realm!token-name`
- Token Secret. The saved secret is not rendered back into the settings form; leave the field blank to keep it unchanged.
- Node, for example `pve-01`
- LXC rootfs storage, for example `local-lvm`. This is required before live LXC deployments can be created.
- LXC network bridge, for example `vmbr0`. Leave empty if containers should be created without an automatic DHCP `net0`.

No Proxmox secrets are committed to the repository.

## Configure A Live Proxmox Link

Use this section when moving from mock mode to a real Proxmox VE node.

### 1. Prepare Proxmox Templates

The plugin only provisions from approved templates. Proxmox remains the source for actual QEMU VM templates and LXC OS templates, while the plugin stores an approved template policy row with display name, OS type, CPU, RAM, disk, default TTL, template type, and either a Proxmox template VMID or an LXC `ostemplate` reference.

Open the **Templates** section on the **Support Provisioning** admin page to:

- see approved plugin templates
- import QEMU VM templates from the configured Proxmox node
- import LXC templates from the configured node's storage `vztmpl` content
- edit the policy values used during provisioning
- remove templates from new provisioning without breaking historical deployment records

The plugin lists QEMU templates from `/nodes/{node}/qemu` and LXC templates from storage content with `content=vztmpl`.

Seeded example template IDs:

| Plugin template | Proxmox template VMID |
| --- | ---: |
| Turnkey PBX Test Appliance | `9001` |
| Windows Support Client | `9002` |
| Linux Utility VM | `9003` |

The fastest first test is to create one real Proxmox QEMU template or download one LXC OS template, then import it from **Support Provisioning > Templates**.

QEMU template requirements:

- The VM must be converted to a Proxmox template.
- The template must exist on the Proxmox node configured in the plugin.
- The plugin currently performs a full clone (`full=1`), so the target storage must have enough free capacity.
- CPU and memory are set by the plugin after clone based on the approved template policy row.
- For IP address display, install and enable `qemu-guest-agent` inside the guest and enable the guest agent option on the VM/template.

LXC template requirements:

- The OS template must be visible as `vztmpl` storage content on the configured Proxmox node.
- Configure **LXC rootfs storage** in the plugin before deploying live containers.
- Configure **LXC network bridge** if containers should receive a DHCP `eth0` during creation.
- The rootfs storage must have enough free capacity for the approved template disk size.

### 2. Create A Dedicated Proxmox API User

In Proxmox, create a dedicated user instead of using `root@pam` for the plugin.

Example via Proxmox shell:

```bash
pveum user add wp-support@pve --comment "WordPress Support Provisioning Portal"
```

You can also create the user in the Proxmox UI under **Datacenter > Permissions > Users**.

### 3. Create An API Token

In the Proxmox UI:

1. Go to **Datacenter > Permissions > API Tokens**.
2. Click **Add**.
3. User: `wp-support@pve`
4. Token ID: `support-portal`
5. Enable **Privilege Separation**.
6. Save the token secret immediately. Proxmox only shows it once.

The plugin stores these fields separately:

- **Token ID**: `wp-support@pve!support-portal`
- **Token Secret**: the secret value shown by Proxmox

Do not include the `=` between token ID and secret in the WordPress settings. The plugin builds the required `PVEAPIToken=tokenid=secret` header internally.

### 4. Grant Proxmox Permissions

Proxmox permissions are path based. For a first controlled test, grant the token access only to the node/storage/template area you intend to use.

The plugin needs permissions for:

- getting the next VMID
- listing QEMU templates on the configured node
- listing LXC template content on the node's storages
- cloning a template VM
- creating LXC containers from an OS template
- changing CPU and memory after clone
- setting Proxmox tags on created VMs and containers
- starting and stopping VMs and containers
- deleting VMs and containers
- reading VM and container status
- reading guest-agent network interfaces for IP display
- reading LXC interfaces for IP display

Practical first-test option:

```bash
pveum aclmod / -user wp-support@pve -role PVEVMAdmin
```

If the clone fails because storage permissions are missing, also grant datastore access on the storage path used by your templates/clones:

```bash
pveum aclmod /storage/local-lvm -user wp-support@pve -role PVEDatastoreAdmin
```

For a tighter production setup, create a custom role with only the required privileges and assign it to the API token or user:

```bash
pveum role add SupportProvisioner -privs "VM.Allocate VM.Audit VM.Clone VM.Config.CPU VM.Config.Disk VM.Config.Memory VM.Config.Network VM.Config.Options VM.PowerMgmt Datastore.AllocateSpace Datastore.Audit Sys.Audit"
pveum aclmod / -user wp-support@pve -role SupportProvisioner
```

Depending on your Proxmox version and storage layout, you may need to scope storage permissions separately, for example:

```bash
pveum aclmod /storage/local-lvm -user wp-support@pve -role SupportProvisioner
```

### 5. Check Proxmox TLS

The plugin uses WordPress HTTP requests to call Proxmox. That means PHP/WordPress must trust the Proxmox HTTPS certificate.

Recommended:

- Use a DNS name for Proxmox, not a raw IP address.
- Install a valid certificate on Proxmox, or install your internal CA certificate so the WordPress/PHP host trusts it.

If Proxmox still uses its default self-signed certificate, WordPress may reject the request with an SSL/cURL certificate error. Fix the trust chain before debugging plugin credentials.

### 6. Configure WordPress Plugin Settings

In WordPress admin, open **Support Provisioning** and use the **Proxmox Settings** section:

| Setting | Value |
| --- | --- |
| Mode | `HTTP token auth` |
| Base URL | `https://proxmox.example.internal:8006` |
| Token ID | `wp-support@pve!support-portal` |
| Token Secret | token secret from Proxmox |
| Node | Proxmox node name, for example `pve-01` |
| LXC rootfs storage | target container rootfs storage, for example `local-lvm` |
| LXC network bridge | bridge for DHCP `net0`, for example `vmbr0` |

The **Node** value must match the Proxmox node name exactly as shown in the Proxmox UI under **Datacenter**.

### 7. First Live Test Checklist

Before creating a deployment:

- Confirm WordPress can reach Proxmox on TCP `8006`.
- Confirm the Proxmox base URL opens from the WordPress server.
- Confirm the configured node name is exact.
- Confirm the selected plugin template points to a real Proxmox template VMID or visible LXC `vztmpl` item.
- For LXC tests, confirm **LXC rootfs storage** is configured.
- Confirm the token has clone/create, VM config, power, delete, audit, and datastore permissions.
- Confirm target storage has enough capacity for the VM clone or LXC rootfs.
- Confirm RAM contingents in WordPress are not blocking the selected template.

Then test in this order:

1. Switch plugin mode to `HTTP token auth`.
2. Create a deployment from **Linux Utility VM** or whichever template VMID you mapped.
3. Open the deployment detail view.
4. Click **Start**.
5. Wait for the guest to boot.
6. Click **Refresh IPs**.
7. Confirm IP addresses appear.
8. Click **Stop**.
9. Click **Delete** when done.

### 8. Common Failure Points

| Symptom | Likely cause |
| --- | --- |
| `Missing Proxmox HTTP configuration` | One of Base URL, Token ID, Token Secret, or Node is empty. |
| `Proxmox request failed with HTTP 401` | Token ID/secret is wrong, token was deleted, or the secret was copied incorrectly. |
| `Proxmox request failed with HTTP 403` | Token exists but lacks permissions for clone/config/power/delete/storage. |
| `Proxmox request failed with HTTP 404` | Node name, template VMID, or LXC template reference is wrong, or the template is on a different node/storage. |
| `Missing LXC rootfs storage configuration` | An LXC template was selected but no target rootfs storage is configured in plugin settings. |
| SSL/cURL certificate error | WordPress/PHP does not trust the Proxmox certificate. |
| Deployment created but no IPs | Guest agent is missing/disabled for a VM, the VM/container has not finished booting, or the LXC bridge/DHCP path is not configured. |
| Start is blocked | Deployment is `EXPIRED`; prolong the TTL first. |

## Design Notes

- Templates are the only provisioning path.
- Resource limits come from approved templates, not user input.
- Deployment lifecycle operations are routed through a dedicated Proxmox client interface and include the stored resource type so VM and LXC actions use the correct Proxmox API path.
- Live Proxmox access can be replaced or expanded without changing REST or UI code.
- WordPress users are used as actors for audit logging. Portal permissions are assigned per user inside the plugin so users created by SSO or external identity providers do not need WordPress author/editor roles.

## Original Bootstrap Brief

The application is an internal self-service portal for support staff. It provisions standardized VMs on **Proxmox VE** through a backend service. Users must never interact with Proxmox directly. The application MUST run as a WordPress plugin.

Typical resources:

- turnkey PBX/test appliances
- Windows support clients
- Linux utility VMs

The system must enforce:

- template-based provisioning only
- policy-controlled resource limits
- audit logging
- lifecycle actions
- TTL / expiration support
