# NexCMP 1.6.1 — full documentation > Complete NexCMP documentation in one file. Source: https://nexcmp.com/docs/ · generated 2026-11-02. --- # What is NexCMP NexCMP is software you install on **your own server** to turn cloud infrastructure into a product you can sell. It gives you: - a **customer portal** where people sign up, add credit, order virtual machines and other services, open a console, take snapshots and contact support; - an **admin console** (`/admin`) where you manage customers, plans, prices, payments, infrastructure and settings; - **billing**: a wallet per customer, hourly or monthly charging, invoices, promotions and resource quotas; - **white-label resellers**: other brands that sell your capacity under their own domain; - an **API**, a command-line tool (`cmp`) and an MCP server for automation; - optional **AI Chat** (support assistant) and **AI Agent** (manage a VM in plain language). ## What it connects to NexCMP does not host virtual machines itself. It drives the platform that does. A *source* (also called a provider) is one of: | Source | Typical use | Guide | |---|---|---| | Apache CloudStack | Private or public cloud with zones, networks, virtual routers and public IPs | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) | | Proxmox VE | Single servers or clusters you already run | [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) | Other modules need their own infrastructure: [bare-metal servers](https://nexcmp.com/docs/infrastructure/dedicated/), [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/), [S3 object storage](https://nexcmp.com/docs/infrastructure/s3/), [Kubernetes and Private Cloud](https://nexcmp.com/docs/infrastructure/kubernetes-and-private-cloud/). See [Infrastructure requirements](https://nexcmp.com/docs/infrastructure/requirements/) for one table covering all of them. ## How it is built NexCMP is a single program with two systemd services on one Ubuntu server: - `nexcmp` — the web server: customer portal, admin console, API, setup wizard; - `nexcmp-vmd` — the background worker: provisioning jobs, synchronisation with your CloudStack or Proxmox, billing runs. Data lives in `/var/lib/nexcmp` (SQLite databases and files), configuration in `/etc/nexcmp`, the program in `/opt/nexcmp`. See [Architecture](https://nexcmp.com/docs/infrastructure/architecture/) for the full picture. ## Where to go next 1. Check the [requirements](https://nexcmp.com/docs/getting-started/requirements/). 2. Get a license — [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/). 3. [Install](https://nexcmp.com/docs/getting-started/installation/), then run the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/). 4. Connect your platform: [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) or [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). --- # Requirements for the NexCMP server This page covers the server that runs NexCMP. What your CloudStack, Proxmox VE or other infrastructure needs is in [Infrastructure requirements](https://nexcmp.com/docs/infrastructure/requirements/). ## Server | Item | Requirement | Checked by the installer | |---|---|---| | Operating system | Ubuntu **22.04** or **24.04** | Yes — stops with an error on anything else | | CPU architecture | x86_64 | Yes | | Memory | 2 GB minimum (the installer refuses less than about 1.8 GB) | Yes | | Free disk | 8 GB free on `/` minimum | Yes | | Privileges | root (`sudo`) | Yes | | Packages | `curl`, `ca-certificates`, `zstd`, `tar`, `openssl`, `python3`, `openssh-client` — installed automatically | Installed if missing | | With HTTPS | `nginx` and `certbot` — installed automatically unless you pass `--no-nginx` | Installed if missing | ::: tip Sizing A small installation (a few hundred customers) runs comfortably on 2 vCPU / 4 GB RAM. Disk use is dominated by the SQLite databases and the optional update and backup copies — plan 20 GB or more for production. ::: ## Network | Direction | Port | Purpose | |---|---|---| | Inbound | 80/tcp | Let's Encrypt validation and redirect to HTTPS | | Inbound | 443/tcp | Customer portal, admin console, API | | Inbound | 3000/tcp | Setup wizard on first install and emergency access only — see [Installation](https://nexcmp.com/docs/getting-started/installation/#the-panel-port-3000) | | Outbound | 443/tcp to `nexcmp.com` | License activation, daily license check, updates | | Outbound | to your CloudStack API / Proxmox API | Provisioning and synchronisation | | Outbound | 443/tcp to payment, e-mail and AI providers you enable | Stripe, PayPal, SMTP, AI provider and so on | The server needs a **public IPv4 address** if you want Let's Encrypt to issue a certificate directly. If the domain is proxied by Cloudflare, see [DNS and Cloudflare](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/). ## DNS - One **main domain** (for example `cloud.example.com`) — the portal is `https://cloud.example.com/` and the admin console is `https://cloud.example.com/admin`. - Optional: `admin.` and `partner.`. If those names resolve to the server (or to Cloudflare), the installer adds them to the certificate. - The domain must match the one registered on your license. ## Not required on this server NexCMP runs as an ordinary unprivileged service user (`nexcmp`). It does **not** need root at runtime, Docker, a separate database server, Redis, or any hypervisor software. --- # License and purchase > **Where in the admin:** System › Admin › License & updates (`/license`) NexCMP is activated with a **license key** that you manage in your [NexCMP account](https://nexcmp.com/licenses). Editions and current prices are listed on [nexcmp.com/pricing](https://nexcmp.com/pricing); this page explains how the license behaves once you have one. ## Get a license 1. Create an account at [nexcmp.com](https://nexcmp.com/register) and verify your e-mail. 2. Choose an edition: - **NexCMP Free** — for network operators with their own ASN and a subnet registered in their own name. Send an application at [nexcmp.com/deploy](https://nexcmp.com/deploy) (company, ASN, subnet, panel domain). Your account e-mail must be on the company domain that the public registry (WHOIS) lists for the ASN or the subnet. Each application is reviewed; you get an e-mail when it is decided, and the license key appears only after approval. - **NexCMP Source** — a one-time upgrade, no ASN needed. See [nexcmp.com/pricing](https://nexcmp.com/pricing). 3. Open your license page. It shows the **license key** (format `NXC-XXXXX-XXXXX-XXXXX-XXXXX`), the **main domain**, and a ready-made install command. ::: warning Keep the key private The license key activates an installation. Treat it like a password: do not paste it into tickets, chats or screenshots. ::: ## What a license contains | Field | Meaning | |---|---| | Main domain | The hostname your portal runs on. The key is bound to it at activation. | | Reseller domains | Extra hostnames served as white-label portals — see [Resellers](https://nexcmp.com/docs/customers/resellers/). | | Updates until | Date up to which you receive **updates and bug fixes**. A Free license starts with **1 year** from the day it is approved; renew it for $99 per year on your license page. Security fixes are always delivered, even after this date. | | Status | `active`, or suspended / expired / revoked. | ## When the free year ends The software **keeps running** when the "Updates until" date passes: customers, services and billing are not affected. You stop receiving new feature releases and bug-fix updates (security fixes still arrive). Renew for $99 per year on your license page at [nexcmp.com/licenses](https://nexcmp.com/licenses) to continue; the renewal adds 12 months to the current end date. NexCMP Free includes bug fixes only — no technical support and no apps. ## What NexCMP Source adds A Source license is a one-time $1,999 upgrade of your license: the full source code, all NexCMP apps (Android, iOS, Windows, macOS, Linux, Chrome / Edge / Brave and Firefox) built and branded for you, and 30 working days with an expert engineer — free migration from VMware, WHMCS or another panel, or a full A-to-Z setup. New source releases are included for 12 months; renewing them afterwards ($99 per year) is optional, and the source you downloaded is yours to keep. See the [Source license terms](https://nexcmp.com/legal/source-license/). ## How the installation checks its license - On install the server activates the key for your domain and receives a **signed token**. - The token is renewed automatically every **24 hours**. It stays valid for **14 days**, so a short Internet outage is harmless. - All releases and tokens are verified with an Ed25519 signature built into the program; a tampered download is rejected. Check the state any time in the admin console or with `sudo nexcmp status`. ## If the license is not valid When the token expires, or the license is suspended or the domain no longer matches: - customers **cannot create new services** and the admin console shows a banner; - services that are already running, **billing and existing customers are not affected**; - fix the cause (reconnect the server to the Internet, renew at [nexcmp.com/licenses](https://nexcmp.com/licenses)) and click **Check now** in License & updates. ## Moving to a new server or domain Activate on the new server with the same key and the registered domain (see [Installation](https://nexcmp.com/docs/getting-started/installation/)). To change the registered domain, do it on your license page at [nexcmp.com/licenses](https://nexcmp.com/licenses) first, then re-run the installer with the new `--domain`. --- # Install NexCMP The installer is a single script served by your license server. It checks the machine, activates your license, downloads and verifies the program, installs the services and (optionally) sets up nginx with a Let's Encrypt certificate. ## Before you start - A server that meets the [requirements](https://nexcmp.com/docs/getting-started/requirements/), and root access. - Your **license key** and **main domain** from your [license page](https://nexcmp.com/licenses) — see [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/). - A DNS record for the domain — see [DNS and Cloudflare](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/). You can also install first and fix DNS later; the installer tells you what is missing and can simply be run again. ## Install 1. Log in to the server and become root (or prefix with `sudo`). 2. Run the command from your license page. It looks like this: ```bash curl -fsSL https://nexcmp.com/install.sh | sudo bash -s -- \ --license NXC-XXXXX-XXXXX-XXXXX-XXXXX \ --domain cloud.example.com \ --email admin@example.com ``` 3. Wait for the checkmarks. A fresh install takes a few minutes; the first start creates the database schema and can take up to a minute on slow disks. 4. Read the summary at the end. It prints the portal and admin URLs, the **setup code**, and the next step. ![The installer finishing with the portal URL, admin URL and one-time setup code](https://nexcmp.com/docs/images/getting-started/installation-01-finish.webp) ## Parameters | Parameter | Default | Meaning | |---|---|---| | `--license KEY` | required on first install | The license key. | | `--domain NAME` | required on first install | Main domain. Must match the license. `https://` and trailing paths are stripped. | | `--email ADDRESS` | none | Contact e-mail for Let's Encrypt expiry notices. Without it the certificate is registered without e-mail. | | `--port N` | `3000` | Local port of the panel (1–65535). | | `--no-nginx` | off | Do not install or configure nginx / TLS. Use this when you run your own reverse proxy or want to look at NexCMP first. | | `--behind-cloudflare` | auto-detected | Force "the domain is proxied by Cloudflare" mode. Normally detected from DNS. | | `--channel NAME` | `stable` | Release channel. | Running the installer again **without parameters** reuses the saved ones (`/etc/nexcmp/install.conf`) and upgrades safely, keeping data, configuration and accounts. That is exactly what `sudo nexcmp update` does. ## What the installer does 1. **Checks the machine** — Ubuntu 22.04/24.04, x86_64, at least 2 GB RAM, 8 GB free disk. 2. **Installs packages** it needs (`curl`, `zstd`, `openssl`, `python3`, `openssh-client` …). 3. **Creates the service user** `nexcmp` and the directories: `/opt/nexcmp` (program, owned by root, read-only to the service), `/etc/nexcmp` (configuration), `/var/lib/nexcmp` (data), `/var/log/nexcmp`. 4. **Activates the license** for the domain and stores a fixed instance ID for this machine. 5. **Downloads the newest release you are entitled to**, then verifies its SHA-256 and its Ed25519 signature. A release without a valid signature is refused. It never downgrades a newer running version. 6. **Writes configuration and systemd units** (`nexcmp`, `nexcmp-vmd`, `nexcmp-update`) and starts the services; it waits for `/healthz` to answer. 7. **Sets up nginx and TLS** (unless `--no-nginx`): obtains a Let's Encrypt certificate with certbot (webroot challenge) for the domain and for `admin.` / `partner.` when those names also resolve. If DNS does not point to this server (or Cloudflare) yet, it skips this step and says so. 8. **Prints a summary** with URLs and, if no administrator exists yet, the **setup code**. ## The panel port (3000) - During first install the panel listens on `0.0.0.0:3000` so you can open the setup wizard at `http://:3000/setup`. - Once the wizard is finished **and** TLS is active, the panel listens on `127.0.0.1` only; nginx proxies `https://` to it. - For emergencies (nginx broken, DNS wrong) you can open the port for a limited time: `sudo nexcmp open-port 30` (30 minutes, then it closes itself) — or use an SSH tunnel: `ssh -L 3000:127.0.0.1:3000 ` and browse `http://127.0.0.1:3000/`. ::: warning Plain HTTP Port 3000 is plain HTTP. Use it only for the setup wizard on a trusted network, or through an SSH tunnel. Do not leave it open. ::: ## Installing without a public domain (lab or test) You can install on a machine with no DNS yet: ```bash curl -fsSL https://nexcmp.com/install.sh | sudo bash -s -- \ --license NXC-XXXXX-XXXXX-XXXXX-XXXXX --domain lab.example.com --no-nginx ``` Then open the wizard through an SSH tunnel (`ssh -L 3000:127.0.0.1:3000 `, browse `http://127.0.0.1:3000/setup`). Re-run the installer without `--no-nginx` once DNS exists. ## Docker and Kubernetes There is no published container image. The installer (a native systemd installation) is the supported installation method. ## Verify ```bash sudo nexcmp status ``` You should see the version, `nexcmp: active`, `nexcmp-vmd: active`, `healthz: ok`, and — until the wizard is done — the setup code. Continue with the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/). If something fails, see [Installation errors](https://nexcmp.com/docs/troubleshooting/installation-errors/). --- # DNS and Cloudflare NexCMP needs one DNS name — its **main domain** — and works in two modes. ## Mode A: DNS points straight at the server 1. Create an **A** record `cloud.example.com → ` (and an **AAAA** record if the server has IPv6). 2. Optional, if you use them: `admin.cloud.example.com` and `partner.cloud.example.com` pointing at the same IP. 3. Run (or re-run) the [installer](https://nexcmp.com/docs/getting-started/installation/). It sees that the name resolves to the server's own public IP and obtains a Let's Encrypt certificate through the HTTP-01 challenge on port 80. ## Mode B: the domain is proxied by Cloudflare Create the same records with the orange cloud (**Proxied**). The installer detects that the name resolves only to Cloudflare addresses and switches to Cloudflare mode: - nginx still listens on 80/443 on your server; - the visitor's real IP is read from the `CF-Connecting-IP` header — **only** for connections coming from Cloudflare's published ranges, so it cannot be forged by anyone else; - the certificate is obtained through Let's Encrypt (Cloudflare forwards the challenge to your origin); if that is not possible yet, a **self-signed origin certificate** is created so that "Full" mode works, and a later re-run of the installer upgrades to Let's Encrypt. You can force this mode with `--behind-cloudflare`. ::: important Set SSL/TLS mode in Cloudflare In Cloudflare go to **SSL/TLS › Overview** and select **Full (strict)** once the installer reports a Let's Encrypt certificate. With a self-signed origin certificate use **Full**. **Flexible** will cause redirect loops. ::: ::: tip If "Always Use HTTPS" is on, Cloudflare sends the validation request to port 443 of your origin. The installer handles this by creating the temporary self-signed certificate first, then retrying Let's Encrypt. ::: ## Check it ```bash dig +short cloud.example.com # A record (a Cloudflare address in mode B) curl -I https://cloud.example.com/healthz sudo certbot certificates # certificate name, domains and expiry ``` Certificates renew automatically with certbot's timer. Renewal uses the webroot `/var/www/nexcmp-acme`, so port 80 must stay reachable (directly or through Cloudflare). ## Common problems | Symptom | Cause | Fix | |---|---|---| | Installer says "DNS … does not point to … (nor to Cloudflare) — skipping nginx/TLS" | DNS record missing, wrong, or not propagated | Fix the record, wait for propagation, re-run the installer. | | Browser shows `ERR_TOO_MANY_REDIRECTS` behind Cloudflare | SSL mode is **Flexible** | Set **Full (strict)** (or **Full**). | | Cloudflare error 525/526 | Origin certificate rejected | Use **Full** with a self-signed origin certificate, or **Full (strict)** with Let's Encrypt. | | Visitors all show Cloudflare IPs in logs | Not in Cloudflare mode | Re-run the installer with `--behind-cloudflare`. | More in [Installation errors](https://nexcmp.com/docs/troubleshooting/installation-errors/). --- # Setup wizard > **Where in the admin:** the wizard is a standalone page at `/setup` shown only until the first administrator exists. Afterwards the same checks live under System › Admin › Start (`/setup`). The wizard turns a fresh installation into a working panel in four short steps. It is served on port 3000 (`http://:3000/setup`) or, when TLS is already active, at `https:///setup`. ::: warning The wizard disappears after step 3 Once the administrator is created the wizard is closed and the setup code is deleted. To reach the panel afterwards use the admin console. ::: ## Step 0 — Setup code The installer prints a **one-time setup code** at the end. You can read it again with: ```bash sudo nexcmp status ``` Paste it into **Setup code** and click **Continue**. The code protects the wizard: nobody who finds the open port can create the administrator without it. ![Step 0 of the setup wizard asking for the one-time setup code](https://nexcmp.com/docs/images/getting-started/setup-wizard-00-code.webp) ## Step 1 — License | Field | Value | |---|---| | License key | `NXC-XXXXX-XXXXX-XXXXX-XXXXX` — pre-activated by the installer, so this step normally shows "Activated for cloud.example.com" | | Main domain | The domain registered on the license | If the installer already activated the license, the wizard recognises it and moves on. Otherwise enter the key and domain and click **Activate**. Errors are shown in red — see [Installation errors](https://nexcmp.com/docs/troubleshooting/installation-errors/). ![Step 1 of the setup wizard showing the license already activated for the main domain](https://nexcmp.com/docs/images/getting-started/setup-wizard-01-license.webp) ## Step 2 — CloudStack (or Connect later) You have two choices: - **Connect now.** Enter the CloudStack **API URL** (`https://cloudstack.example.com/client/api`), **API key** and **Secret key**, click **Test connection**, then select the **Zone**, the **Guest network for customer VMs**, a **Zone display name** and the two-letter **Country**. Click **Save CloudStack**. - **Connect later.** Click **Connect later**. Everything else works; you connect CloudStack or Proxmox VE afterwards under Infrastructure. Until then the health panel reminds you that no source is connected. ![Step 2 of the wizard with the CloudStack API URL, API key and secret key fields filled with example values](https://nexcmp.com/docs/images/getting-started/setup-wizard-02-cloudstack-form.webp) ::: important Use a dedicated CloudStack account Do not use the ROOT administrator key. Create a dedicated domain and account for NexCMP and use its API key — see [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/#create-a-dedicated-account-and-api-key). The wizard checks that the chosen network belongs to the zone and to the account of that key. ::: ![After Test connection: the zone and guest network lists, zone display name and country fields](https://nexcmp.com/docs/images/getting-started/setup-wizard-02b-cloudstack-picked.webp) If you plan to use **Proxmox VE** only, choose **Connect later** and follow [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). ## Step 3 — Administrator and brand | Field | Rules | |---|---| | Admin email (login) | Valid e-mail address. This is your administrator user name. | | Password | At least 12 characters, with letters and digits. | | Brand name | Shown to your customers (default "Cloud"). Up to 60 characters. | | Support email | Defaults to the admin e-mail. | ![Step 3 of the wizard with administrator e-mail, password, brand name and support e-mail](https://nexcmp.com/docs/images/getting-started/setup-wizard-03-admin.webp) Click **Finish setup**. The panel restarts (about 1–2 seconds) and the page sends you to the admin login at `https:///admin/`. If that address does not answer yet (DNS or TLS pending), it sends you to `/` on the current address instead. ![The wizard after completion with the message that the panel is restarting](https://nexcmp.com/docs/images/getting-started/setup-wizard-03-done.webp) ## What the wizard sets up for you - Brand name, site URL (`https://`), admin URL (`https:///admin`), console URL (`https:///console`), support e-mail and sender name. - Default settings for the modules that ship with NexCMP: DDoS & BGP, AI and web SSH are **off**; bandwidth metering is **on**. See [Modules](https://nexcmp.com/docs/infrastructure/modules/). - If you connected CloudStack: provider #1, its first zone, the guest network, and an operations key pair that is attached to customer VMs so you can support them. Next: [First login](https://nexcmp.com/docs/getting-started/first-login/). --- # First login and next steps > **Where in the admin:** the login page is `/admin`; after sign-in the landing page is Overview › Dashboard. ## Addresses | What | URL | |---|---| | Customer portal | `https:///` | | Admin console | `https:///admin` | | Setup / emergency by IP | `http://:3000/` (see [the panel port](https://nexcmp.com/docs/getting-started/installation/#the-panel-port-3000)) | | Health check | `https:///healthz` | ## Sign in 1. Open `https:///admin`. 2. Enter the administrator e-mail and password you created in the wizard. 3. You land on the **Dashboard**. A banner at the top appears if the license is not valid or no source is connected. ![The admin login page](https://nexcmp.com/docs/images/getting-started/first-login-01-login.webp) ## Recommended checklist Work through these in order — each links to its page. | # | Task | Page | |---|---|---| | 1 | Turn on two-factor authentication for yourself | [Security](https://nexcmp.com/docs/administration/security/) | | 2 | Connect an infrastructure source (if you chose "Connect later") | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) or [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) | | 3 | Check zones and the machine templates / OS list | [Zones](https://nexcmp.com/docs/infrastructure/zones/), [Templates](https://nexcmp.com/docs/infrastructure/templates/) | | 4 | Review plans and prices | [Plans](https://nexcmp.com/docs/catalog/plans/) | | 5 | Set your logo, colours and legal pages | [Branding](https://nexcmp.com/docs/customers/branding/), [Legal](https://nexcmp.com/docs/customers/legal/) | | 6 | Configure outgoing e-mail and send a test | [SMTP and emails](https://nexcmp.com/docs/administration/smtp-and-emails/) | | 7 | Enable at least one payment channel and test a top-up | [Payment channels](https://nexcmp.com/docs/billing/payment-channels/) | | 8 | Decide which modules you sell | [Modules](https://nexcmp.com/docs/infrastructure/modules/) | | 9 | Place a test order as a customer and delete it | [Ordering](https://nexcmp.com/docs/portal/ordering/) | | 10 | Make a first backup | [Backup and restore](https://nexcmp.com/docs/getting-started/backup-and-restore/) | ## Create your first order (smoke test) 1. Open the customer portal in a private window and register a test account. 2. Add credit (a voucher or a test payment). 3. Order the smallest plan in your zone. 4. Watch it appear in the admin under Services › Virtual Machines, open its console, then destroy it. If the VM does not reach *running*, open **Infrastructure › Cloud provider › Sync & errors** and read the error. --- # Updating NexCMP > **Where in the admin:** System › Admin › License & updates (`/license`) → *Update* card. ## What you receive | Release type | Delivered when | |---|---| | Security fix | **Always**, even after your "updates until" date | | Feature release | Only if it was published **on or before** your "updates until" date | You can see both dates under **License & updates**. Renew the entitlement at [nexcmp.com/licenses](https://nexcmp.com/licenses). ## Update from the admin console 1. Open **System › Admin › License & updates**. 2. In the **Update** card click **Check for update**. The card shows the current version, the newest version you may install, a *security* tag when relevant, and the changelog. 3. Click **Update to X.Y.Z** and confirm. The program downloads the release, verifies its **SHA-256 and Ed25519 signature**, replaces the binary atomically (keeping a `.bak` copy) and restarts. Expect about **10 seconds** of downtime. 4. Reload the page and check the version in the top-right of License & updates. ![The Update card with the running version and the Check for new version button](https://nexcmp.com/docs/images/getting-started/updating-01-card.webp) ## Update from the command line ```bash sudo nexcmp update ``` This runs the installer that is shipped *inside the currently installed release* (not a script downloaded at that moment) with the saved parameters from `/etc/nexcmp/install.conf`. It refreshes the binary, the systemd units, nginx templates and the node agent, and keeps data, configuration and accounts. ::: tip Never downgrades If the server offers a version older than the one running, the installer keeps the running version. ::: ## Before you update 1. Take a backup: `sudo nexcmp backup` — see [Backup and restore](https://nexcmp.com/docs/getting-started/backup-and-restore/). 2. Read the changelog in the Update card. For changes since 1.3, see [Release notes through 1.6.1](https://nexcmp.com/docs/getting-started/release-notes/), including the VPN agent update and Bare Metal pricing rules. 3. Update during a quiet period; running virtual machines are not touched, but customers cannot use the portal for a few seconds. ## After you update - Run `sudo nexcmp status` and confirm both services are active. - Open the admin Dashboard and the **Needs attention** page. - Database migrations run automatically on first start of the new version. - If you run VPN nodes, update their agent separately and check the usage readings. Updating the panel makes the new agent available; it does not prove that every remote node is already running it. See [VPN node data allowance](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/#data-allowance-on-a-vpn-node). If an update fails, see [Update errors](https://nexcmp.com/docs/troubleshooting/update-errors/). --- # Release notes > **Where in the admin:** System › Admin › License & updates (`/license`), the **Update** card. These notes cover changes after 1.3 through **NexCMP 1.6.1**. Back up before updating and use the release offered by your installation's **Update** card. See [Updating NexCMP](https://nexcmp.com/docs/getting-started/updating/) for the commands and signature checks. ## 1.6.1 - **Bare Metal metrics:** the customer **Overview** shows CPU, RAM, disk activity and network tiles with recent history. Missing measurements show a dash and a reason. Network readings require a traffic counter; they are not inferred from iDRAC hardware inventory. - **Bare Metal sample plans:** sample names use `BM-40C-`. Built-in sample plans that you have not edited receive neutral names. Edited plans are preserved, and a referenced plan keeps its code. Check the actual server hardware before making a plan sellable. - **Plan changes:** the admin request list sorts on the server and keeps its actions visible while scrolling horizontally. - **Report a bug:** the form scrolls with its buttons kept visible. Password masking preserves the surrounding diagnostic text. - **Loading:** page resources and commonly used admin data are loaded earlier to reduce navigation delays. See [Bare Metal setup, pricing and customer controls](https://nexcmp.com/docs/infrastructure/dedicated/) and [Report a bug](https://nexcmp.com/docs/administration/report-a-bug/). ## 1.6.0 - **Bare Metal setup:** enter the iDRAC networks, ISO server address and panel callback address in **Safety › Management network connection**. Sample values block live operations until configured. - **ISO hosting:** `sudo nexcmp dedicated-setup-iso` sets up the management-network ISO web server. The installer can install the console and ISO tools with `--baremetal` or the ISO options. Missing tools block live operations with `tools_missing`. - **Bare Metal prices:** a fresh or unused installation starts with USD prices. An installation that already has VND pricing data keeps VND and still requires a USD/VND rate. Updating does not reinterpret those existing prices as dollars. - **Customer server page:** renew in advance, schedule a billing-cycle change, request a plan change, read invoices and view the OS login after installation. A new owner cannot see an earlier owner's password. - **Customer profiles:** eight tabs group overview, services, billing, quota, security, support, affiliate and logs. Services share one searchable, sortable table. - **Service actions:** lock, unlock, cancel without refund or cancel with refund use a common dialog. Refunds go back to the original wallet or credit allocation; payment processors are not refunded automatically. See [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/) and [Managing customers](https://nexcmp.com/docs/customers/managing-customers/). BYOIP and attaching a customer subnet to a Bare Metal server remain unavailable in NexCMP. ## 1.5.2 The main administrator can open **Report a bug** next to **Docs** and send a ticket to the license account without a separate sign-in. Diagnostics are optional. Staff, customers and AI assistants cannot send reports through this action. See [Report a bug](https://nexcmp.com/docs/administration/report-a-bug/). ## 1.5.1 - **VPN usage:** agent nodes report traffic to the panel. A subscription that exhausts its GB allowance is cut off; it becomes available again after a bandwidth upgrade or a new period. Nodes without usage reports show **Not measured** and are not cut off based on GB. - **VPN agent:** version 1.5.1 retries unacknowledged usage, sends final traffic on shutdown and closes open connections when an endpoint is stopped. Update remote agents as well as the panel. - **Retired VPN add-ons:** High IP Reputation, High Concurrency and High Priority Network are no longer sold. Their fees are removed from existing subscriptions' future renewal amounts; past periods are not refunded. If an add-on was deleted from the catalog, check the renewal amount manually. - **Protocol labels:** the IPv4 HTTP proxy is plain HTTP. IPv4 addresses do not rotate. The IPv6 proxy's port 443 serves SOCKS5; it is not an HTTPS proxy endpoint. - **Fresh setup:** database initialization retries transient migration conflicts between the panel and worker so saving the first provider does not require a restart. See [VPN and IPv6 proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/). ## 1.5.0 **AI Models** is an optional gateway and billing module, off by default. Add model sources you operate or are authorized to resell, sync models, set prices and routes, enable the models, then open sales. There are no bundled provider accounts, models or prices. It is separate from the AI Chat and AI Agent module. See [AI Models](https://nexcmp.com/docs/infrastructure/ai-models/). ## 1.4.2 IPv6 proxy node installation uses the daemon and network script shipped in the release package. The panel needs `sshpass` for password-based SSH installation. The panel runs rotation schedules, and node setup excludes the gateway address from allocations. Branding can set a custom VPN service display name. See [VPN and IPv6 proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/). ## 1.4.0–1.4.1 - **AI administration:** admin AI Chat, personal AI accounts, admin API keys, the admin MCP server, OAuth connectors, configuration import/export and the setup checklist are available. See [Configure with AI](https://nexcmp.com/docs/configure-with-ai/overview/). - **Licensing:** Free and Source workflows include official builds and custom source builds. Account, licensing, downloads and documentation use `nexcmp.com`. See [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/). - **Documentation links:** **Docs** in the admin opens the guide for the current page. - **Currencies:** a new installation follows the customer's language by default. USD/VND comes from a public exchange-rate table or your fixed rate. See [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). - **Mixed-source zones:** public Cloud Server prices work when CloudStack and Proxmox VE share a zone. Plans use their own infrastructure source. - **Prices derived from offerings:** the percentage is a margin on the selling price. A 40% margin on a cost of $60 yields $100, before any rounding. ## Upgrade checklist 1. Back up, read the **Update** card, update and run `sudo nexcmp status`. 2. Update VPN agents and confirm the nodes report measured traffic before relying on GB cut-off. 3. If using Bare Metal, check the management-network fields, required tools, price unit, templates and sample plans before opening sales or live operations. 4. Open a customer profile and verify its service list. Check the amount and original payment allocation before confirming a cancellation with refund. 5. Keep optional modules off until their infrastructure is ready. For AI Models, verify a provider, a priced model and a successful customer request before opening sales. --- # Backup and restore > **Where in the admin:** System › Admin › Backup (`/backup`) appears only when the DDoS & BGP module is enabled; for a standard installation use the command line below. ## What is backed up `sudo nexcmp backup` creates one compressed archive containing: | Part | Content | |---|---| | `data/**/*.db` | Every SQLite database in `/var/lib/nexcmp`, copied with SQLite's online backup API — consistent even while the panel is running | | `files.tar` | All other files in `/var/lib/nexcmp` (settings files, branding uploads, `secrets/`), except databases and the `updates/` folder | | `etc/` | A copy of `/etc/nexcmp` (installer settings, instance ID, port and bind) | ::: warning The archive contains your master keys `secrets/` holds the key that encrypts API keys, payment credentials and SMTP passwords stored in the database. The archive is created with permissions `0600`. Store it encrypted and off the server. Without these keys a restored database cannot decrypt its secrets. ::: ## Take a backup ```bash sudo nexcmp backup # writes to /var/backups/nexcmp sudo nexcmp backup /mnt/nas/nexcmp # or to another directory ``` The command prints the archive path, for example `/var/backups/nexcmp/nexcmp-backup-20261030-120000.tar.zst`. ### Schedule it ```bash echo '17 3 * * * root /usr/local/bin/nexcmp backup /var/backups/nexcmp && find /var/backups/nexcmp -name "nexcmp-backup-*" -mtime +14 -delete' | sudo tee /etc/cron.d/nexcmp-backup ``` Copy the folder off the machine (object storage, NAS, another server). ## Restore Restore onto a server where NexCMP of the **same version (or newer)** is installed. 1. Install NexCMP with the [installer](https://nexcmp.com/docs/getting-started/installation/) using the same license and domain, but do **not** run the setup wizard. 2. Stop the services: ```bash sudo systemctl stop nexcmp-vmd nexcmp ``` 3. Unpack the archive into a temporary directory: ```bash sudo mkdir /root/restore && sudo tar --zstd -xf nexcmp-backup-20261030-120000.tar.zst -C /root/restore ``` 4. Put the files back: ```bash sudo tar -xf /root/restore/files.tar -C /var/lib/nexcmp sudo cp -a /root/restore/data/. /var/lib/nexcmp/ sudo cp -a /root/restore/etc/. /etc/nexcmp/ sudo chown -R nexcmp:nexcmp /var/lib/nexcmp ``` 5. Start the services and check: ```bash sudo systemctl start nexcmp nexcmp-vmd sudo nexcmp status ``` 6. Sign in to `https:///admin` with the administrator of the *backed-up* system. ::: tip Rehearse it Do step 1–6 on a spare server once, before you need it. A backup you have never restored is only a hope. ::: ## Not included - nginx configuration and certificates (regenerated by the installer; Let's Encrypt certificates are re-issued). - Your CloudStack / Proxmox VE servers and the virtual machines on them — back those up with their own tools. --- # Uninstall NexCMP ::: warning Customers lose access Uninstalling stops the portal and billing. Virtual machines on your CloudStack or Proxmox VE keep running, but nothing manages or bills them. Make a [backup](https://nexcmp.com/docs/getting-started/backup-and-restore/) first. ::: ## Remove the program, keep the data ```bash sudo nexcmp uninstall ``` This disables and removes the `nexcmp` and `nexcmp-vmd` services, the nginx site and snippet, `/opt/nexcmp` and the `nexcmp` command. **Data stays** in `/var/lib/nexcmp` and `/etc/nexcmp`, so a later install on the same server continues where you stopped. ## Remove everything ```bash sudo nexcmp uninstall --purge ``` Additionally deletes `/var/lib/nexcmp`, `/etc/nexcmp`, `/var/log/nexcmp` and the `nexcmp` system user. This cannot be undone. ## After uninstalling - Let's Encrypt certificates and the `nginx` / `certbot` packages are left in place. Remove the certificate with `sudo certbot delete --cert-name ` if you want. - Release the domain on your license page at [nexcmp.com/licenses](https://nexcmp.com/licenses) if you will activate it elsewhere. - Delete the dedicated CloudStack account / Proxmox API token created for NexCMP if you no longer need them. --- # `nexcmp` command reference The installer puts a helper at `/usr/local/bin/nexcmp`. Run it on the NexCMP server. Commands that change the system need `sudo`. (The customer-side tool for the API is a different program, see [cmp CLI](https://nexcmp.com/docs/developers/cmp-cli/).) | Command | Needs root | What it does | |---|---|---| | `nexcmp status` | No (more detail with `sudo`) | Version, service state (`nexcmp`, `nexcmp-vmd`), `/healthz`, license summary, public-port state, setup code if the wizard is not finished | | `nexcmp update` | Yes | Runs the installer shipped in the installed release with the saved parameters — see [Updating](https://nexcmp.com/docs/getting-started/updating/) | | `nexcmp backup [dir]` | Yes | Backup to `/var/backups/nexcmp` (or `dir`) — see [Backup and restore](https://nexcmp.com/docs/getting-started/backup-and-restore/) | | `nexcmp uninstall [--purge]` | Yes | Remove services and program; `--purge` also removes data — see [Uninstall](https://nexcmp.com/docs/getting-started/uninstall/) | | `nexcmp open-port [minutes]` | Yes | **Emergency**: listen on the panel port (default 3000) on all interfaces for 1–1440 minutes (default 30), then close automatically | | `nexcmp close-port` | Yes | Close the emergency window now | | `nexcmp fix-owner` | Yes | Give files in `/var/lib/nexcmp` that belong to root back to the `nexcmp` user | ## `nexcmp status` ```text Version : 1.6.1 nexcmp : active nexcmp-vmd : active healthz : ok License : active · domain cloud.example.com · token valid until 2026-11-12 · updates until 2027-10-30 Public port: 127.0.0.1 only (nginx proxy; emergency: sudo nexcmp open-port) ``` If files in the data directory are owned by root (for example created by running another `nexcmp` command with `sudo` as root), `status` prints a warning, because the service user cannot read them — a typical symptom is the license showing HTTP 421. Fix with `sudo nexcmp fix-owner`. The same fix runs automatically at every service start. ## Emergency access When the domain, nginx or TLS is broken you can still reach the panel: ```bash sudo nexcmp open-port 30 # http://:3000/ for 30 minutes sudo nexcmp close-port # close it earlier ``` Safer, without opening anything: `ssh -L 3000:127.0.0.1:3000 user@server` and browse `http://127.0.0.1:3000/`. ## Environment | Variable | Used for | |---|---| | `NEXCMP_BASE` | Override the license server base URL (default `https://nexcmp.com`); for testing only | Files and directories: program `/opt/nexcmp`, configuration `/etc/nexcmp`, data `/var/lib/nexcmp`, logs `/var/log/nexcmp` and `journalctl -u nexcmp -u nexcmp-vmd`. --- # Infrastructure requirements > **Where in the admin:** Infrastructure (the left menu zone) and System › Admin › License & updates (`/license`) for turning modules on. This page lists what each module needs **outside** the NexCMP server. For the server itself (Ubuntu, memory, disk, ports) see [Requirements for the NexCMP server](https://nexcmp.com/docs/getting-started/requirements/). To turn modules on, see [Modules](https://nexcmp.com/docs/infrastructure/modules/). ## Module by module | Module | Minimum infrastructure | Details | |---|---|---| | **Virtual Machines on CloudStack** | Apache CloudStack 4.23 (tested), KVM, an advanced zone with a guest network and a public IP range; a dedicated domain and account with an API key (never ROOT); at least one ready template and one service offering | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) | | **Virtual Machines on Proxmox VE** | Proxmox VE 9.x (verified on 9.2); an API token with a custom role; a storage that clones; a bridge; an IPv4 block with a gateway; one cloud-init template per OS and per node | [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) | | **OS templates** | CloudStack: a Linux build machine with libguestfs and a web server for the image. Proxmox VE: cloud-image templates with the guest agent | [OS templates](https://nexcmp.com/docs/infrastructure/templates/) | | **Bare metal / Dedicated Server** | Dell servers with iDRAC 9 reachable over HTTPS (Redfish) from the panel on a management network; an HTTP server on that network that serves ISO files; writable ISO folders for the NexCMP service | [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/) | | **AI Models** | One or more model sources you are authorized to resell, with an OpenAI-compatible or Anthropic-compatible API and your own key; outbound HTTPS (port 443, public domain name) from the panel to each source, or an internal gateway listed under Allowed internal hosts; no GPU or special hardware | [AI Models](https://nexcmp.com/docs/infrastructure/ai-models/) | | **VPN** | One or more Linux hosts (x86_64, systemd, root) with public IPv4 addresses on the interface or routed to it, outbound HTTPS to your panel | [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/) | | **IPv6 Proxy** | Linux hosts (x86_64, systemd, `nft`, `ip`, `ss`, `sysctl`) with a public IPv4, ports 80, 443 and 60001-65535 free, and an IPv6 prefix from /32 to /64 that works for outbound traffic; SSH access with a password for the one-time install | [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/) | | **Object Storage (S3)** | A Ceph cluster with RADOS Gateway (RGW), an Admin Ops user, and a public HTTPS endpoint on port 443 | [S3](https://nexcmp.com/docs/infrastructure/s3/) | | **Kubernetes** | CloudStack with the Kubernetes Service (CKS) enabled, at least one Kubernetes version ISO, and a free control-plane service offering of exactly 2 vCPU and 2048 MB | [Kubernetes and Private Cloud](https://nexcmp.com/docs/infrastructure/kubernetes-and-private-cloud/) | | **Private Cloud** | CloudStack with a VPC offering and one network offering per bandwidth plan (plus a locked 1 Mbps offering) | [Kubernetes and Private Cloud](https://nexcmp.com/docs/infrastructure/kubernetes-and-private-cloud/) | | **OS & ISO Images** | CloudStack whose secondary storage VM can reach the Internet to download customer images (direct links, no redirects); the CloudStack account may register templates and ISOs | [Modules](https://nexcmp.com/docs/infrastructure/modules/) | | **Bandwidth** | Nothing extra with the default source: usage is read from CloudStack VM statistics. VMs on Proxmox VE are not metered | [Bandwidth](https://nexcmp.com/docs/catalog/bandwidth/) | | **AI Chat and AI Agent** | An OpenAI-compatible API (base URL, model and key) that the panel server can reach. No extra software on your servers | [AI providers](https://nexcmp.com/docs/ai/ai-providers/) | | **DDoS & BGP** | An add-on package that needs an edge router host with daemons that run as root (BGP, filtering, flow statistics). It is not part of the standard installation | [Modules](https://nexcmp.com/docs/infrastructure/modules/) | | **DDoS Shield** | The DDoS & BGP add-on | [Modules](https://nexcmp.com/docs/infrastructure/modules/) | | **Web SSH terminal** | A separate SSH proxy service and a route for it. It is not part of the standard installation; the AI Agent does not need it | [Modules](https://nexcmp.com/docs/infrastructure/modules/) | ## Network paths at a glance | From | To | Port | Purpose | |---|---|---|---| | Panel server | CloudStack management server | 443 or 8080 (as you publish it) | API | | Panel server | Proxmox VE node | 8006/tcp | API and console | | Panel server | Dell iDRAC | 443/tcp (and the VNC port, default 5901) | Redfish and console | | iDRAC | ISO web server | the HTTP port you choose (for example 8080) | Virtual media | | Panel server | Ceph RGW | 443/tcp | Admin Ops | | Panel server | IPv6 proxy node | SSH port you enter | One-time install | | VPN and proxy nodes | Panel (through nginx) | 443/tcp | Enrollment, heartbeat, configuration | | Customers | VPN and proxy nodes | per-IP ports 65001-65535 (VPN); 80, 443, 60001-65535 (IPv6 proxy) | The service itself | ::: tip Keep management networks private iDRAC and hypervisor management addresses should be on a network customers cannot reach. NexCMP refuses iDRAC addresses outside the private ranges you allow. ::: ## Checklist before you sell 1. The infrastructure behind the first service is connected and **Test connection** is green. 2. A zone is ready to sell (see [Zones](https://nexcmp.com/docs/infrastructure/zones/)): an active backend, a plan and a ready template. 3. A payment channel is configured (see [Payment channels](https://nexcmp.com/docs/billing/payment-channels/)). 4. E-mail works (see [SMTP and e-mails](https://nexcmp.com/docs/administration/smtp-and-emails/)). 5. You placed one test order and deleted the VM. --- # Architecture > **Where in the admin:** this is a reference page. The services described here are visible with `sudo nexcmp status`; connections are configured under Infrastructure and System › Settings. ## The picture ![NexCMP architecture: customers reach nginx through DNS and optionally Cloudflare, nginx proxies to the panel on port 3000, the panel and the worker call out to CloudStack, Proxmox VE, iDRAC, Ceph, payment gateways, SMTP and an AI provider, and VPN and IPv6 proxy nodes talk back to the panel](https://nexcmp.com/docs/images/infrastructure/architecture.svg) ### The same diagram in words 1. A customer or administrator opens `https://cloud.example.com` (the portal), `https://cloud.example.com/admin` (the admin console) or calls the REST API. DNS points the name at your server, or at Cloudflare, which proxies to it. 2. **nginx** on the panel server listens on ports 80 (redirect and certificate renewal) and 443 (TLS) and forwards every request to the **panel** on `127.0.0.1:3000`. After the setup wizard the panel listens on the loopback interface only. 3. The **panel** (`nexcmp.service`) serves the customer portal, the admin console, the REST API, the setup wizard and the downloads that nodes need (the agent binary, the install script, the `cmp` command-line tool). It also checks your license once a day. 4. The **worker** (`nexcmp-vmd.service`) listens on `127.0.0.1:8112`, which is local only. It runs everything that takes time or must happen on a schedule: provisioning jobs, synchronisation with CloudStack and Proxmox VE, billing ticks, bandwidth collection and the browser console connections. 5. From these two processes NexCMP calls outward: **CloudStack** (management API), **Proxmox VE** (API on port 8006, certificate pinned), **Dell iDRAC** (Redfish over HTTPS on the management network), **Ceph RGW** (S3 and Admin Ops over HTTPS), **payment gateways** (Stripe, PayPal, USDT and bank-transfer providers, which also call your webhook URLs), **your SMTP server**, an **AI provider** with an OpenAI-compatible API, and optionally **Telegram**. 6. **VPN nodes** run `cmp-agent`. Each node enrolls once with a one-time token, then sends a heartbeat every 5 seconds over HTTPS to the panel and receives its configuration in the reply. **IPv6 proxy nodes** are installed once by the panel over SSH, then pull their configuration from the panel. 7. State lives in SQLite databases and a few files in `/var/lib/nexcmp`. Credentials for CloudStack, Proxmox, S3, iDRAC and the AI provider are stored encrypted (AES-256-GCM) with master key files in the same area. ## Services and processes | Process | Runs as | Listens on | Does | |---|---|---|---| | `nginx` | root (workers `www-data`) | 80, 443 | TLS, redirect, proxy to the panel | | `nexcmp` | `nexcmp` | 3000 (all interfaces during first install, then `127.0.0.1`) | Portal, admin console, API, wizard, downloads | | `nexcmp-vmd` | `nexcmp` | `127.0.0.1:8112` | Jobs, sync, billing, bandwidth, console | | `nexcmp-update` | root | none | Applies updates the admin console requests (a path unit waits for the request) | | `cmp-agent` (on VPN nodes) | root | the VPN listener ports | Reports to the panel, runs proxy listeners on public IPv4s | The panel and the worker run as an ordinary user without any Linux capabilities, with a read-only file system except `/var/lib/nexcmp` and `/var/log/nexcmp`. They need no root, no Docker, no separate database and no Redis. ## Files and where they are | Path | Content | |---|---| | `/opt/nexcmp` | The program: releases, `bin/`, shared files such as the template builder. Owned by root, read-only to the service. | | `/etc/nexcmp` | Installer settings (`install.conf`), port and bind (`nexcmp.env`). | | `/var/lib/nexcmp` | SQLite databases, uploaded branding, the agent download folder, license state, the master key files. Back this up. | | `/var/log/nexcmp` | Logs. | See [Backup and restore](https://nexcmp.com/docs/getting-started/backup-and-restore/). The master key files must be backed up together with the databases, or a restored database cannot decrypt its stored credentials. ## Who connects to whom | Direction | Path | Port | Notes | |---|---|---|---| | Customer to panel | Internet, DNS, optional Cloudflare, nginx | 443 | See [DNS and Cloudflare](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/). | | Panel to license server | `nexcmp.com` | 443 | Activation, a daily check and updates. | | Panel to CloudStack | API | 443 or 8080 | The VM console, bandwidth and Kubernetes features use the same endpoint. | | Panel to Proxmox VE | API and WebSocket console | 8006 | Certificate pinned by SHA-256. | | Panel to iDRAC | Redfish | 443, VNC 5901 | Management network only. | | iDRAC to ISO server | HTTP | your port | The iDRAC pulls the ISO. | | Panel to Ceph RGW | Admin Ops | 443 | | | Panel to SMTP | SMTP | 587 or 465 | | | Panel to AI provider | HTTPS | 443 | Only when the AI module is on. | | Payment gateway to panel | Webhook | 443 | Stripe and PayPal call back. | | Node to panel | `cmp-agent` | 443 | Enroll, heartbeat, upgrade. | | Panel to IPv6 proxy node | SSH | the port you enter | One-time install only. | ## Why it matters - **A panel that can be reached is a panel that can be attacked.** Keep port 3000 closed once the setup wizard is done. The installer already does this when TLS is active. - **The worker must be running** for anything to be created or billed. `sudo nexcmp status` shows both services and the health check. - **Plan outbound firewalls.** If the panel server's outbound traffic is filtered, allow the destinations in the table above. - **Management traffic stays off the public path.** Connections to CloudStack, Proxmox VE, iDRAC and Ceph go straight from the panel server to those systems, never through nginx or Cloudflare. --- # Modules > **Where in the admin:** System › Admin › License & updates (`/license`), the card **Module**. ## What it does A module is a service or feature that you can switch on or off as a whole. A module that is **off** is hidden from the admin menu and customers cannot buy it; its API routes answer `404 module_disabled` where the module is an add-on. Turning a module on only exposes the feature: the infrastructure behind it still has to be connected (see the last column). ## Requirements | Item | Requirement | |---|---| | Account | An administrator with write access. Read-only staff can see the switches but not change them. | | Infrastructure | See the table: each module lists what it needs. | | Depends on | Some modules need others (DDoS Shield needs DDoS & BGP). | ## Prepare the infrastructure Not required for the switch itself. Connect the infrastructure of each module as described on the page linked in the table. ## Connect in NexCMP 1. Open **System › Admin › License & updates** and scroll to the card **Module**. It says that disabled modules are hidden from the admin menu and cannot be purchased by customers. 2. Use the switch of a module. The page confirms **Saved: reloading menu** and reloads, so the menu changes at once. ![The Module card in License & updates with a switch for each module](https://nexcmp.com/docs/images/infrastructure/modules-01-license-modules.webp) ### The modules | Module (as shown) | Default | What it adds | Needs | Page | |---|---|---|---|---| | **Virtual Machines** | always on | VM catalog, orders, console, snapshots | CloudStack or Proxmox VE | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/), [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) | | **Bandwidth** | **on** | Quotas, add-ons, alerts and overage actions for VMs, Kubernetes and S3 | Nothing extra (CloudStack VM statistics) | [Bandwidth](https://nexcmp.com/docs/catalog/bandwidth/) | | **OS & ISO Images** | off | Customers register their own OS images and ISOs, billed per GB | CloudStack; Internet access for its secondary storage VM | [Requirements](https://nexcmp.com/docs/infrastructure/requirements/) | | **Kubernetes** | off | Kubernetes clusters (CKS) | CloudStack with CKS enabled | [Kubernetes and Private Cloud](https://nexcmp.com/docs/infrastructure/kubernetes-and-private-cloud/) | | **Private Cloud** | off | VPC with dedicated-bandwidth plans | CloudStack VPC and network offerings | [Kubernetes and Private Cloud](https://nexcmp.com/docs/infrastructure/kubernetes-and-private-cloud/) | | **Object Storage** | off | S3 accounts on Ceph RGW, pay as you go | Ceph RGW and a zone with the S3 service | [S3](https://nexcmp.com/docs/infrastructure/s3/) | | **VPN** | off | VPN on your own nodes | Nodes running `cmp-agent` | [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/) | | **IPv6 Proxy** | off | IPv6 proxy on your own nodes | Proxy nodes with an IPv6 prefix | [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/) | | **Bare Metal** | off | Dell servers through iDRAC: orders, OS install, console, power | iDRAC 9 on a management network | [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/) | | **AI Models** | off | A gateway that sells access to AI models: one key for many models, per-model prices, usage limits, logs, wallet billing | Model sources you bring: an OpenAI-compatible or Anthropic-compatible API with your own key | [AI Models](https://nexcmp.com/docs/infrastructure/ai-models/) | | **AI** | off | AI Chat (automated support) and AI Agent (VM administration in plain language) | An OpenAI-compatible provider | [AI providers](https://nexcmp.com/docs/ai/ai-providers/) | | **DDoS Shield** | off | IP protection for customers; needs DDoS & BGP, so its switch stays disabled without it | The DDoS & BGP add-on | below | | **DDoS & BGP** | off, **add-on** | Edge router console: BGP, RTBH, XDP, sessions and edge traffic | An edge host with root daemons | below | ### Which switch does what - **Bare Metal** has two layers. The module switch shows the admin menu and the API. Selling to customers is a separate switch with a typed confirmation under **Services › Dedicated Server**, and every write to an iDRAC is blocked until you turn on **live ops** under **Infrastructure › Bare Metal › Safety**. See [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/). - For **Kubernetes**, **Private Cloud**, **VPN**, **IPv6 Proxy** and **Images** the module switch and the sales switch on the service page act on the same setting. - **AI** also needs a provider configured under **System › Settings › AI provider**. - **AI Models** has two layers. The module switch shows the admin menu and the API. Selling to customers is a separate sale switch at the top of the AI Models pages, off by default. See [AI Models](https://nexcmp.com/docs/infrastructure/ai-models/). ### Modules read at start-up Three features are not ordinary switches. They are read **once when the services start** and need infrastructure that the standard installation does not include. | Feature | Setting | State | What it needs | |---|---|---|---| | DDoS & BGP | `module_ddos` | Shown as **Add-on package** when off, **Installed** when on. There is no switch in the console. | Edge-router daemons (BGP, RTBH, XDP, flow statistics) that run as root. The panel runs as an unprivileged user and cannot run them. | | AI | `module_ai` | A normal switch. The routes follow it at once; AI Chat's background jobs (ticket investigation, scheduled replies) start at the next restart of the services. The console says a restart is recommended. | The AI provider. | | Web SSH terminal | `feature_ssh_web` | Off by default, no switch in the console | A separate SSH proxy service and its route. Without it the portal's Console tab shows only VNC. The **AI Agent does not need it**: it runs commands over an SSH connection that the panel makes itself. | After a change to a start-up setting, restart the services: `sudo systemctl restart nexcmp nexcmp-vmd`. ## Verify 1. The menu entry of the module appears (or disappears) after the reload. 2. For a customer-facing module, the portal shows the new menu entry for a signed-in customer. A module that is off shows nothing or "coming soon". ## Limits and notes - Turning a module off does not delete anything. Running VMs, clusters, S3 accounts and subscriptions keep running and billing; customers just cannot create new ones. - Quota pages list only the services whose modules are on. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | The menu entry did not appear | The page was not reloaded | Reload the admin console. | | DDoS Shield switch is disabled | DDoS & BGP is not installed | DDoS Shield needs the DDoS & BGP add-on. | | "This module is an add-on package and cannot be switched here" (HTTP 409 or 422) | You (or a script) tried to switch DDoS & BGP | It is an add-on package; there is no switch. | | "DDoS Shield needs the DDoS & BGP add-on" | Same, for DDoS Shield | Install the add-on first. | | A module route answers 404 `module_disabled` | The module is off | Switch the module on. | | AI Chat answers but ticket investigation never starts | The AI module was switched on without a restart | `sudo systemctl restart nexcmp nexcmp-vmd`. | | Read-only staff cannot save | Their role is read-only | Use an administrator account. | --- # Zones > **Where in the admin:** Infrastructure › Datacenter › Zone (`/infra/zones`). A zone's detail page is `/infra/zones/`. ## What it does A **zone** is the location a customer chooses when ordering: a country and city with a flag, plus the services sold there. It is a sales concept, separate from the zones, clusters and nodes of the platforms behind it. Behind a zone you attach **infrastructure**: a CloudStack zone, a Proxmox VE cluster, VPN nodes with IP ranges, an S3 backend. Zone 1 is created by the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/), with the display name and country you entered. Add more zones when you have infrastructure in more places, or to give VPN and S3 their own locations. ## Requirements | Item | Requirement | |---|---| | Permissions | An administrator account. A read-only staff role can look but not change. | | Infrastructure | A zone sells nothing until something is attached to it (see "Which service sells where" below). | | Depends on | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) or [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) for VMs, [VPN and proxy nodes](https://nexcmp.com/docs/infrastructure/vpn-proxy-nodes/) for VPN, [S3](https://nexcmp.com/docs/infrastructure/s3/) for object storage. | ## Prepare the infrastructure Not required. A zone is a record inside NexCMP. ## Connect in NexCMP ### Create a zone 1. Open **Infrastructure › Datacenter › Zone** and click **Add zone**. 2. Fill in the form: | Field | Example value | Notes | |---|---|---| | Country | `United States` | Chooses the flag. | | City | `New York` | Optional. | | Name (Vietnamese), Name (English) | `New York` | The name customers see in each language. | | Slug | `us-new-york` | a-z, 0-9, hyphen, 2-32 characters. Filled in from country and city until you type your own. | | Visibility | Public | **Public** zones are visible to every customer; **Hidden** zones are only given by an administrator. | | Order | `0` | Lower numbers first in lists. | | Services sold in this zone | Virtual Machines (VM), VPN, Object Storage (S3) | Tick at least one. | | Active | On | **Off** hides the zone from customers. | | Default | off | The zone preselected for customers who have not chosen. Exactly one zone is the default. | ![Add zone dialog with a country, city, slug and the services ticked](https://nexcmp.com/docs/images/infrastructure/zones-01-add-zone.webp) 3. Click **Create zone**. The list shows the zone with its flag, owner, services, state and counts of VMs, plans and attached infrastructure. ![Zone list with the default zone and a second zone](https://nexcmp.com/docs/images/infrastructure/zones-02-list.webp) ### Look at what is attached Click a row to open the zone. The tab **Provider infrastructure** lists each attached backend with its provider, kind (CloudStack or Proxmox VE), region, state and priority. The tab **VPN infrastructure** lists the VPN ranges and nodes of the zone and links to the VPN pool. ![Zone detail with the Provider infrastructure tab](https://nexcmp.com/docs/images/infrastructure/zones-03-detail.webp) ### Which service sells where | Service | How it gets into a zone | |---|---| | Virtual Machines on CloudStack | The wizard (or **Infrastructure › Cloud provider › CloudStack**) attaches CloudStack to zone 1. | | Virtual Machines on Proxmox VE | On the cluster's **Clusters** tab, **Attach cluster to zone**. The cluster joins an **existing** zone, normally the one that sells through CloudStack. | | VPN | A VPN node that reports its public IP ranges creates (or joins) a zone for its country and puts the ranges in the VPN pool. You can also assign the zone and node of a range under **VPN IP pools**. | | IPv6 Proxy | A new IPv6 proxy node creates its zone in the **off** state. Turn the zone on here so customers can pick it. | | Object Storage (S3) | Tick the S3 service on the zone, then attach the backend under **Ceph S3 (RGW)**. | ### When can a zone sell virtual machines? A zone shows the tag **not ready to sell VMs** until it has at least one active, sellable backend **and** at least one visible plan **and** at least one ready template on that backend. The customer order form lists only zones that are ready. ## Verify 1. The zone appears in the list as **On**, with the right flag. 2. Its detail page shows the backend or the VPN ranges you expect. 3. For VMs: the tag **not ready to sell VMs** is gone. In the customer portal, start an order and check that the zone is offered. ## Limits and notes - In this NexCMP release customers order virtual machines in the **default zone** (zone 1). Other zones are used for VPN, IPv6 Proxy and Object Storage, or to prepare a location before it has infrastructure; the note under the zone list says the same. - A zone can be deleted only when nothing is attached: no backends, VMs, plans or VPN ranges. The default zone cannot be deleted; make another zone the default first. - Zones owned by a reseller can only be turned on or off and hidden or shown by you; their other fields are locked. - The slug is part of internal references. Choose it once. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Zone name is required" / "Select country" / "Select at least 1 service" | A required field is empty | Fill the name, choose the country and tick a service. | | "Invalid slug (a-z 0-9 -, 2-32 chars)" | The slug has other characters or the wrong length | Use lowercase letters, digits and hyphens only. | | Tag "not ready to sell VMs" | No active sellable backend, no plan or no ready template in that zone | Attach infrastructure, then add a plan and a template. See [Plans](https://nexcmp.com/docs/catalog/plans/) and [Templates](https://nexcmp.com/docs/infrastructure/templates/). | | Delete is disabled with "still attached" | The zone has backends, VMs, plans or VPN ranges | Remove or move them first. | | Delete is disabled with "Default zone" | Zone is the default | Set another zone as default. | | Customers do not see the zone | It is hidden, inactive, or has no service ticked | Set Visibility to Public, Active to On and tick the service. | | An S3 zone is missing from the backend form | The S3 service is not ticked on the zone | Edit the zone and tick **Object Storage (S3)**. | --- # CloudStack > **Where in the admin:** Infrastructure › Cloud provider › CloudStack › API connection (`/infra/cloudstack?tab=connect`). The first connection can also be made in the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/), step 2. ## What it does NexCMP sells virtual machines that run on your Apache CloudStack cloud. It deploys, starts, stops, resizes, snapshots and deletes VMs through the CloudStack API, hands out public IP addresses from the zone, reads usage for billing and bandwidth, and shows capacity and health in the admin console. CloudStack stays the source of truth for the hypervisors; NexCMP is the storefront, billing and portal on top. Use this page when CloudStack is (or will be) your main VM source. For a second source next to it, see [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). ## Requirements | Item | Requirement | |---|---| | CloudStack version | 4.23 is the tested release. The VM console needs 4.18 or newer and live CPU/RAM scaling of a running VM needs 4.23 with KVM. | | Zone type | A zone with at least one guest network for customer VMs and a public IP range with free addresses. | | Hypervisor | KVM is the tested hypervisor. | | Account | A **dedicated domain and account** created for NexCMP. Never use the ROOT administrator key. | | API reachability | The panel server must reach the management server API URL (`https://cloudstack.example.com/client/api`) over HTTP(S). The VM console, bandwidth and Kubernetes features use the same endpoint. | | Optional read-only key | A second, read-only key lets the admin console show hosts, storage, system VMs and public IP ranges. Not needed for selling VMs. | | Public IP | Free public IPv4 addresses in the zone. The prefix that NexCMP may sell is configured under Sales IP pools. | | Templates | At least one ready, password-enabled template and one service offering that your account can see. See [Templates](https://nexcmp.com/docs/infrastructure/templates/). | | Storage | Primary storage for the account's volumes and, if you sell snapshots or images, secondary storage. | | Depends on | Nothing. Kubernetes, Private Cloud, Images and Bandwidth depend on this page. | ::: warning Do not use the ROOT administrator key Create a dedicated domain and account for NexCMP. The key then reaches only that domain, and a leaked key cannot touch the rest of your cloud. The setup wizard also checks that the network you choose belongs to the zone **and** to the account that owns the key. ::: ## Prepare the infrastructure The commands use [CloudMonkey](https://cloudstack.apache.org/) (`cmk`) logged in as a CloudStack administrator. Replace the example names and the placeholders in angle brackets. ### Create a dedicated account and API key 1. Create the domain that will hold everything NexCMP owns. ```bash cmk create domain name=nexcmp ``` Note the `id` in the output; it is used as `` below. 2. Find the role you want to give the account. ```bash cmk list roles type=DomainAdmin filter=id,name ``` | Role | When to choose it | |---|---| | **Domain Admin** (recommended) | Full feature set: VMs, networks, projects for Kubernetes, VPC, images. Its reach stops at the `nexcmp` domain. | | User | VM lifecycle only. A plain user cannot expunge VMs unless the global setting `allow.user.expunge.recover.vm` is true; NexCMP then stops the VM and CloudStack expunges it after `expunge.delay`. | 3. Create the account and its first user. Use a long random password; it is never typed again. ```bash cmk create account name=nexcmp-api username=nexcmp-api \ password='CHANGE-ME-LONG-RANDOM' email=ops@example.com \ firstname=NexCMP lastname=API roleid= domainid= ``` 4. Generate the API key pair for the new user. ```bash cmk list users domainid= username=nexcmp-api filter=id,username cmk generate userkeys id= ``` Copy `apikey` and `secretkey`. They are the **API key** and **Secret key** you enter in NexCMP. NexCMP stores them encrypted and shows only the last four characters afterwards. 5. Make sure the domain has no restrictive resource limits (instances, volumes, public IPs, networks, snapshots, templates, projects). ```bash cmk list resourcelimits domainid= ``` ### Create the guest network for customer VMs Customer VMs are deployed into one guest network that belongs to the new account. An isolated network with a virtual router (VR) and the Static NAT, Firewall and Port Forwarding services works well, because NexCMP attaches a public IP to each VM with static NAT. ```bash cmk list networkofferings state=Enabled guestiptype=Isolated filter=id,name cmk create network name=customers displaytext=customers zoneid= \ networkofferingid= account=nexcmp-api domainid= ``` Check the default egress policy of the offering: if it is "deny", customer VMs have no outbound access until firewall egress rules are added. ### Make offerings and templates visible - **Service offerings** and **network offerings** must be public or assigned to the `nexcmp` domain, otherwise the account cannot list or use them. - Register or share at least one template (see [Templates](https://nexcmp.com/docs/infrastructure/templates/)). NexCMP lists templates that are ready and featured. ### Optional: read-only key for infrastructure pages The tabs **Capacity**, **Host**, **Storage** and **System VM** and the public IP ranges list use a second key that only needs read access to six commands. Create a limited role instead of reusing a full administrator key: ```bash cmk create role name=nexcmp-readonly type=Admin # is the id printed above for api in listHosts listCapacity listSystemVms listVlanIpRanges listClusters listStoragePools; do cmk create rolepermission roleid= rule=$api permission=allow done cmk create rolepermission roleid= rule='*' permission=deny ``` Then create an account with that role (in the ROOT domain, because it reads zone-wide data), generate its keys like step 4, and enter them as the **RO API key** in the next section. ### Check the network path From the panel server: ```bash curl -fsS -o /dev/null -w '%{http_code}\n' https://cloudstack.example.com/client/api ``` Any HTTP answer (even 4xx) proves the port is reachable. A timeout means a firewall or DNS problem on your side. ## Connect in NexCMP ### In the setup wizard Step 2 of the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/) asks for the same values. **Test connection** lists the zones and networks the key can see; choose the zone and the guest network, a display name for the zone and the country, then click **Save CloudStack**. The wizard rejects a network that is not in that zone or not visible to the key. ![Wizard step 2 with the CloudStack API URL and keys filled in and the zone and network chosen after Test connection](https://nexcmp.com/docs/images/infrastructure/cloudstack-01-wizard-step.webp) ### Later, in the admin console 1. Open **Infrastructure › Cloud provider › CloudStack** and choose the **API connection** tab. 2. Fill in the card **Connection**: | Field | Example value | |---|---| | Endpoint API | `https://cloudstack.example.com/client/api` | | API key (write access) | `XXXX` (leave empty to keep the saved key) | | Secret key | `XXXX` | | RO API key / RO secret key | optional, from the read-only account | | Zone ID | the UUID of your zone | ![The API connection tab with an example endpoint and masked keys](https://nexcmp.com/docs/images/infrastructure/cloudstack-02-connect-form.webp) 3. Click **Test connection** before saving. It tests the keys you typed, without saving them: the write key with `listZones` and `listCapabilities`, and the read-only key with `listSystemVms`. 4. Click **Save connection**. Keep **Enable CloudStack connection** on; switching it off makes the worker stop all CloudStack jobs, without losing data. 5. Open **Services › Virtual Machines › Policy › Provisioning** to set the default guest network, custom offerings and the default key pair for new VMs. ![VM Policy, Provisioning tab, with the default network ID](https://nexcmp.com/docs/images/infrastructure/cloudstack-04-provisioning.webp) 6. Open **Infrastructure › Cloud provider › Sales IP pools** and, under **Configure sales ranges**, set **Sellable IP range prefixes** (for example `203.0.113.`) and **Excluded IP prefixes** if some addresses in the zone must never be sold. Leave the prefix empty to allow every free address. ![Sales IP pools with the sellable prefix 203.0.113. set](https://nexcmp.com/docs/images/infrastructure/cloudstack-05-sales-ip-pool.webp) ### What NexCMP reads and writes | Area | Reads | Changes | |---|---|---| | VMs | list, states, usage history, console endpoint | deploy, start, stop, reboot, reinstall, destroy, scale, reset password and SSH key | | Disks and snapshots | volumes, VM snapshots, backups | create, attach, detach, resize, delete, snapshot, restore | | Networking | networks, routers, public IPs, firewall, port forwarding, load balancer rules | associate and release public IPs, static NAT, firewall, port forwarding, egress rules, networks and ACLs for Private Cloud | | Catalog | service, disk and network offerings, templates, ISOs, OS types | register and delete templates and ISOs (Images), service offerings when creating custom plans | | Kubernetes | supported versions, clusters, configuration | projects, resource limits, networks, clusters (module Kubernetes) | | Read-only key | hosts, clusters, storage pools, system VMs, capacity, VLAN IP ranges | nothing | ### Live CPU and RAM, and bandwidth - **Hot-plug.** A running VM can be resized without a reboot only when its template allows it (dynamically scalable) and the guest brings new CPUs and RAM online. NexCMP turns Auto Scaling on for templates whose name ends in `vN` with N of 6 or more, or when you set it by hand in the template's **vCPU/RAM hot-plug** field. See [Templates](https://nexcmp.com/docs/infrastructure/templates/). - **Bandwidth source.** With the default settings NexCMP measures traffic from CloudStack VM statistics (`listVirtualMachinesUsageHistory`, polled every 5 minutes and summed per VM network interface). The figures include traffic between VMs, so they can be a little higher than an edge router would count. Keep CloudStack VM statistics collection enabled; gaps longer than the statistics retention are skipped. See [Bandwidth](https://nexcmp.com/docs/catalog/bandwidth/). ## Verify 1. **Test connection** returns "Write key: OK" with your zone and the CloudStack version. 2. **Infrastructure › Cloud provider › Providers** shows the provider as active with a recent test time. The dashboard health panel no longer says "CloudStack: not connected". 3. **Sales IP pools** lists free addresses (it refreshes about once a minute). 4. Order a small VM from the customer portal or the admin console and check that it reaches Running, has a public IP and that you can open the console. ## Limits and notes - One CloudStack cluster can be connected in this NexCMP release. The **Add cloud provider** dialog can test a second cluster's keys but cannot save it. - One CloudStack zone is attached to NexCMP's default zone; a CloudStack with several zones is scoped to the zone you enter, so other zones' VMs, IPs and networks are ignored. - Deleting the guest network in CloudStack makes every new deployment fail. The admin console shows a red notice when the network disappears. - Credentials never leave the server. The API returns only the last four characters. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `listCapabilities: cloudstack 401: unable to verify user credentials and/or request signature` | Wrong API key or secret, key from another user, or a clock problem on the panel server | Generate the keys again with `cmk generate userkeys` and paste both. Check the panel server time. | | Test: `listZones: …` or timeout | The panel server cannot reach the API URL | Check DNS, firewall and the exact `/client/api` path. | | Wizard: "network not found in that zone for this API key" | The chosen network belongs to another account or zone | Create the network in the dedicated account (see above) and run **Test connection** again. | | Wizard: "pick a zone and a network" | Test not run, or no zone or network selected | Click **Test connection** and choose both lists. | | Banner "CloudStack key not configured" | No key saved yet ("Connect later") | Fill the Connection card, **Save connection**, then **Test connection**. | | Banner "CloudStack connection is OFF" | The **Enable CloudStack connection** switch is off | Switch it on and save. | | Read-only key: "unusable …" in the test | Missing role permission or wrong account | Check the six allowed commands and that the account is in the ROOT domain. | | "Zone public IP ranges not measured yet" or "no public IP ranges declared" | No read-only key, or the zone really has no public range | Add the read-only key, or add a public IP range in CloudStack. | | "Pool empty — wait 60 s for vmd sync" | The IP list is refreshed by the worker once a minute | Wait a minute and reload. | | "out of Free IPs in range 203.0.113." | Every address in the sellable prefix is taken | Add addresses to the zone, widen the prefix or release unused IPs. | | "Failed to fetch network data (CloudStack not responding or key lacks permissions)" | Key lost access, or CloudStack is down | Run **Test connection**; check the account is enabled. | | "Guest network DOES NOT exist" | The network was deleted in CloudStack | Create the network again and set it as the default under Provisioning. | --- # Proxmox VE > **Where in the admin:** Infrastructure › Cloud provider › Providers (`/infra/providers`), then the provider page (`/infra/providers/`) with the tabs Overview, Clusters, VMs, Servers and Credentials. Plans are set under Services › Virtual Machines › Plans & pricing (`/vm/catalog`). ## What it does NexCMP can sell virtual machines that run on one or more Proxmox VE clusters, next to or instead of CloudStack. A VM is created by cloning an OS template with full clone, then configured through cloud-init: size, IP address from a pool you define, root password and SSH key. Customers get power control, console, snapshots, reinstall, password reset and metrics in the portal. They never see the cluster, node or source name. Use it when you already run Proxmox VE, or to offer a second, differently priced pool of VMs. ## Requirements | Item | Requirement | |---|---| | Proxmox VE | PVE 9.x. The integration was verified on 9.2. It needs the `import` storage content and `import-from` disk option (PVE 8.2 or newer). | | Network from the panel | HTTPS to the cluster API, `https://:8006`. No path, user name or query. The panel and the worker both connect; the browser console is proxied through the panel. | | Account | A dedicated PVE user with an **API token** and a custom role with the privileges listed below. Never use `root@pam`. | | Storage | A storage for VM disks that supports clones (for example `local-lvm`). For the built-in template builder, the storage named `local` with the content types `import` and `snippets`. | | Network | A Linux bridge (for example `vmbr0`) that reaches the network where customer IPs live. Use a VLAN-aware bridge if you enter a VLAN tag. | | IPs | An IPv4 block (/16 to /30) routed to that bridge, with a gateway. NexCMP assigns addresses itself; Proxmox has no IP management. | | Templates | One cloud-init template VM per OS, on every node you deploy to, with the QEMU guest agent. | | Zone | An existing zone to attach the cluster to. See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | Depends on | Nothing. | ## Prepare the infrastructure Run the commands as root on any cluster node. ### 1. Create a least-privilege role, user and API token 1. Create the role. The privilege list is exactly what NexCMP uses; it contains nothing that edits users, storage definitions or the datacenter. ```bash pveum role add NexCMP -privs "VM.Allocate VM.Clone VM.Config.CDROM VM.Config.CPU VM.Config.Cloudinit VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.PowerMgmt VM.Audit VM.Console VM.Snapshot VM.Snapshot.Rollback VM.Backup VM.GuestAgent.Audit VM.GuestAgent.Unrestricted Datastore.Audit Datastore.AllocateSpace Datastore.AllocateTemplate Sys.Audit Sys.AccessNetwork SDN.Use SDN.Audit Pool.Audit Mapping.Audit Mapping.Use" ``` `Sys.AccessNetwork` is required by the cloud-image download. PVE 9 has no `VM.Monitor` privilege; do not add it. 2. Create the user and a token. `--privsep 1` means the token's rights are the **intersection** of the user's and the token's own permissions. ```bash pveum user add nexcmp@pve --comment "NexCMP" pveum user token add nexcmp@pve api --privsep 1 ``` The command prints the token **secret once** (a UUID). Copy it now. The token id is `nexcmp@pve!api`. 3. Give the role to **both** the user and the token. This is the step people miss: with privilege separation only the token gets 403 errors such as `Datastore.Audit` if the user has no permission. ```bash pveum acl modify / --roles NexCMP --users nexcmp@pve pveum acl modify / --roles NexCMP --tokens 'nexcmp@pve!api' ``` 4. Test it from the panel server: ```bash curl -sk -H 'Authorization: PVEAPIToken=nexcmp@pve!api=' https://192.0.2.10:8006/api2/json/version ``` A JSON document with a `version` means the token works. (`-k` is only for this quick check; NexCMP pins the certificate itself, see below.) ### 2. Check the bridge and the storage ```bash pvesm status # storages, type, content and free space ip -br link show type bridge ``` - Customer VMs get `virtio` network interfaces on the bridge you enter in NexCMP. If you set a VLAN, the bridge must be VLAN-aware. - Put customer VMs on a bridge or VLAN **separate from the node's management address**. If they share a network, the Proxmox firewall's automatic management IP set opens ports 22, 8006, 3128, 5900-5999 and 60000-60050 of the node to every VM in that network. If you cannot separate them, add an explicit DROP rule for the VM subnet to those ports above the management ACCEPT rule. ### 3. Storage notes (LVM-thin) - Leave thin-pool **zeroing at its default**. Turning it off (`lvchange -Zn`) can leave old data in newly allocated chunks and corrupt the partition tables or file systems of cloned VMs. - Disks that report "write cache: disabled" can clone at only a few MB/s. Check with `hdparm -W /dev/sdX` and enable the write cache if the disk has power-loss protection. ### 4. Enable the `import` content (only for the built-in template builder) The built-in builder downloads the cloud image into the storage named `local` and creates the template from it. Add `import` (and `snippets`) to that storage's content list. Keep the types you already use; `--content` replaces the whole list: ```bash pvesm status --storage local # note the current content pvesm set local --content iso,vztmpl,backup,import,snippets ``` ### 5. Build an OS template from a cloud image A template is a cloud-init VM that NexCMP clones. Each one is registered under a short **OS key** such as `ubuntu-24.04` with its VM id. Build one template per OS **on every node that will receive VMs**, because the clone runs on the chosen node. **Option A, recommended: build it yourself.** Stock Ubuntu and Debian cloud images do not contain the guest agent and do not allow root login, and NexCMP sets the root password through cloud-init. Prepare the image first: ```bash apt install -y libguestfs-tools wget -O noble.qcow2 https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img virt-customize -a noble.qcow2 --install qemu-guest-agent \ --write /etc/cloud/cloud.cfg.d/99-nexcmp.cfg:$'ssh_pwauth: true\ndisable_root: false\n' \ --write /etc/ssh/sshd_config.d/00-nexcmp.conf:$'PermitRootLogin yes\nPasswordAuthentication yes\n' ``` Then create the template VM (id 9000 is an example; keep template ids below the `vmid from` value, default 1000): ```bash qm create 9000 --name tpl-ubuntu-24-04 --memory 2048 --cores 2 --cpu host --ostype l26 \ --scsihw virtio-scsi-single --net0 virtio,bridge=vmbr0 --agent 1 --serial0 socket --vga serial0 qm set 9000 --scsi0 local-lvm:0,import-from=$PWD/noble.qcow2,discard=on,iothread=1 qm set 9000 --ide2 local-lvm:cloudinit --boot order=scsi0 qm template 9000 ``` **Option B: the built-in builder.** The **Create OS template from cloud image** card on the Clusters tab (see below) downloads a qcow2 image, builds the template and registers it. It creates the VM with cloud-init and the guest agent flag, but it does not change what is inside the image. Use it only with an image that already allows root login and runs the guest agent, or when you also provide a cloud-init vendor-data snippet. The guest agent is needed for **Reset password** and for showing the VM's IP address. ### 6. Know the certificate fingerprint (optional) NexCMP trusts the cluster by pinning the SHA-256 fingerprint of its TLS certificate, so a self-signed certificate is fine. To see the value NexCMP will pin: ```bash openssl x509 -in /etc/pve/local/pve-ssl.pem -noout -fingerprint -sha256 ``` ## Connect in NexCMP 1. Open **Infrastructure › Cloud provider › Providers** and click **Add cloud provider**. Choose **Proxmox VE** and fill in: | Field | Example value | |---|---| | Name | `Proxmox cluster 1` | | Endpoint | `https://192.0.2.10:8006` | | Token id | `nexcmp@pve!api` | | Token secret | `XXXX` (the UUID printed in step 1) | | Slug | `pve-1` (a-z, 0-9, hyphen, 2-32 characters) | | Default node | `pve1` (empty: the online node with the most free RAM) | | Storage | `local-lvm` | | Bridge | `vmbr0` | ![Add cloud provider dialog with Proxmox VE selected and example endpoint, token and storage values](https://nexcmp.com/docs/images/infrastructure/proxmox-01-add-provider.webp) 2. Click **Save & open provider**, enter a reason when asked. The token is encrypted before it is stored. 3. On the **Overview** tab click **Test connection**. The first successful test **pins the cluster's TLS fingerprint**; from then on a different certificate is refused. 4. Open the **Clusters** tab. The card **Node · storage · bridge** lists what the API sees; the card **Cluster configuration** holds the settings: | Field | Meaning | |---|---| | Default node | Node used for new VMs. Empty means the online node with the most free RAM. | | Storage, Bridge, VLAN | Disk storage, NIC bridge and optional VLAN tag (0 means none). | | CPU type | `host` by default. Use `x86-64-v2-AES` when the nodes have different CPU generations. | | vmid from | First VM id given to customer VMs (default 1000). | | Storage ISO, Storage backup | Leave empty unless you use them; empty means the ISO and backup features are not offered. | | Template OS | One per line, `os-key=vmid`, for example `ubuntu-24.04=9000`. | | Pinned TLS fingerprint (SHA-256) | Filled in by the first test. Clear it and test again after you replace the cluster certificate. | ![Clusters tab, Cluster configuration card with example node, storage, bridge and OS template list](https://nexcmp.com/docs/images/infrastructure/proxmox-03-cluster-config.webp) 5. Optional: if you used Option B above, fill **Create OS template from cloud image** (OS key `ubuntu-24.04`, vmid `9000`, node `pve1`, the qcow2 image URL) and click **Create template**. It runs in the background for a few minutes; the result is in the Audit log. ![Create OS template from cloud image card with an example Ubuntu image URL](https://nexcmp.com/docs/images/infrastructure/proxmox-04-template-from-image.webp) 6. Click **Add pool** in **Cluster IP pool** and create the pool that VMs draw addresses from: | Field | Example value | |---|---| | Name | `customers-1` | | CIDR | `198.51.100.0/24` | | Gateway | `198.51.100.1` (never given to a VM) | | DNS | `1.1.1.1,8.8.8.8` (1 to 4 IPv4 addresses) | | Bridge, VLAN | `vmbr0`, `0` | | Already used IPs | one per line, `ip note`, for example `198.51.100.10 monitoring` | NexCMP never assigns the network address, the broadcast address, the gateway or a reserved address. Pools of the same cluster cannot overlap. A pool can be deleted only when none of its addresses is in use. ![Add IP pool dialog with an example /24 and gateway](https://nexcmp.com/docs/images/infrastructure/proxmox-05-ip-pool.webp) 7. In **Attach cluster to zone**, choose the zone and, optionally, a node name, then click **Attach to zone**. The cluster joins the zone that already sells VMs, so one zone can have CloudStack and Proxmox together. The new source is **not sold by default** and does not change how existing plans behave. ![Attach cluster to zone card with an existing zone selected](https://nexcmp.com/docs/images/infrastructure/proxmox-06-attach-zone.webp) 8. Open **Services › Virtual Machines › Plans & pricing** and click **Add plan**. In **Infrastructure source** choose the Proxmox cluster, then set CPU, RAM, disk and price. A Proxmox plan needs no CloudStack offering. To sell the same configuration from both sources, use **Clone** on a plan and choose the other source. The plan's disk must be at least as large as the template's disk; Proxmox cannot shrink a disk. ### Import a VM that already exists Open the **Servers** tab to list the VMs on the cluster. **Import** assigns an existing VM to a customer account at no charge: nothing is billed for hours, power-off time or traffic. The customer can start, stop, reboot and open the console but cannot delete it; only an administrator can. NexCMP only reads from the cluster while importing. - The cluster must be attached to a zone **with the node name** of that VM (leave the node empty and imports report that the region is not set up). - To make a VM **protected** (stop, reboot, reinstall, snapshot restore, destroy and plan change are refused), import it from the command line with `--protect`: ```bash sudo -u nexcmp /opt/nexcmp/bin/nexcmp px-import --provider pve-1 --server-ref pve1/101 --account-id 12 --hostname web-1 --protect ``` ### What is and is not supported on Proxmox | Feature | Proxmox VE | |---|---| | Create, power on/off/reboot, delete | Yes | | Resize CPU and RAM, grow the disk | Yes, but the VM is stopped, changed and started again (no live resize) | | Snapshots | Yes, up to 5 per VM: create, list, restore, delete | | Reinstall | Yes: the VM is rebuilt from the template and keeps its id, IP and settings, with a new password | | Reset password | Yes, through the guest agent | | Console | Yes, a browser console proxied by the panel | | Metrics | Yes: CPU, memory, network and disk | | ISO mount, backups, firewall, IPv6, port forwarding, change IP, extra volumes, Auto Scaling, DDoS Shield, VPC, bandwidth metering | Not available. The customer VM page hides what a source cannot do. | ## Verify 1. **Test connection** shows OK and the provider status is active. 2. **Clusters** lists your nodes as online, with the storage and the bridge. 3. **Cluster IP pool** shows the free, used and held counts. 4. The OS key appears under the zone's templates in **Services › Virtual Machines › Template** after the cluster is attached. 5. Order a test VM from a plan that uses the cluster. It should get an address from the pool, a root password and a working console; the pool shows one address used. ## Limits and notes ::: tip Mixed-source zones Since NexCMP 1.4.0, the public Cloud Server price list also works when CloudStack and Proxmox VE share a zone. Each plan uses its own **Infrastructure source**. After attaching a cluster, check the public price list and place a test order for each source. ::: - Several Proxmox clusters can be added. Each is one provider. - A zone offers one default source; a plan with its own **Infrastructure source** is deployed to exactly that cluster, as long as the cluster is active and in the plan's zone. - A cluster behind the same zone as CloudStack keeps its legacy plans on CloudStack. Test an order end to end before launch. - The API token can only be replaced, never viewed. Replace it in the **Credentials** tab: create the new token in Proxmox first, enter it, run **Test connection**, then revoke the old token. - The Test, Sync and cluster-info actions are limited to six calls per minute per administrator. - NexCMP bills your customers; the hardware and licence cost of the cluster is yours. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Test: "Key rejected (wrong access key / secret, or revoked)" or HTTP 401/403 | Wrong token id or secret, or the ACL was given to the token only | Re-run the two `pveum acl modify` commands (user **and** token) and check the token id is `user@realm!tokenid`. | | Test: "Could not connect to the provider" or "Timed out" | Firewall, wrong host or port | Check `https://host:8006` from the panel server. | | Test: "Invalid endpoint or not on the allowlist" | The endpoint has a path, user, query or is not HTTPS | Use exactly `https://host:8006`. | | "cluster TLS certificate DOES NOT match pinned fingerprint" | The cluster certificate was replaced | Clear **Pinned TLS fingerprint** on the Clusters tab, save, run **Test connection** again. | | Slug rejected | Not a-z, 0-9, hyphen, or not 2-32 characters | Use something like `pve-1`. | | "Token id format: user@realm!tokenid and secret is required" | Token id or secret missing | Enter both; the secret is the UUID printed when the token was created. | | "Invalid template line: …" | A line in **Template OS** is not `key=vmid` | Use letters, digits, `.`, `_`, `-` in the key and a vmid of at least 100. | | "OS key: a-z 0-9 . _ - and vmid ≥ 100" | Invalid key or id in **Create template** | Fix the OS key and use a free vmid. | | "vmid … already exists" when creating a template | The vmid is taken | Choose another id. | | "url points to internal address" | The image URL is an internal IP | Use a public URL; names are resolved by Proxmox. | | Template task fails on download | The storage `local` has no `import` content | `pvesm set local --content …,import,snippets`, then retry. A file name must end in `.qcow2`, `.raw`, `.vmdk`, `.ova` or `.ovf`. | | "cluster has no online nodes" or "node … is not online" | The default node is down or misspelt | Fix **Default node** or leave it empty. | | "image does not belong to cluster" | The plan's OS is not in **Template OS** | Add the key to the list and save. | | Deploy waits, then fails on clone | The template VM is not on the chosen node | Create the template on that node, or set **Default node** to the node that has it. | | Customer cannot log in as root | The image blocks root login | Rebuild the template as in Option A. | | Reset password does nothing | No guest agent in the guest | Install and enable `qemu-guest-agent` in the template. | | "Name, CIDR, and gateway are required" | Missing pool field | Fill all three. | | Pool create: "overlaps with pool …", "gateway must be within range (not network/broadcast address)" or "cidr: network address must be …" | Overlapping pool, wrong gateway, or a CIDR that is not on a network boundary | Use a free range, a gateway inside it and a CIDR written as the network address (for example `198.51.100.0/24`). | | Import: "region is not set up" | The cluster is attached without the VM's node name | Attach the cluster to the zone with the node name. | --- # OS templates and ISOs > **Where in the admin:** Services › Virtual Machines › Template (`/vm/templates`). Proxmox templates are listed per cluster on Infrastructure › Cloud provider › Providers › (cluster) › Clusters. ## What it does A template is the operating-system image a new VM starts from. NexCMP does not store images itself: it reads what CloudStack reports (ready, featured templates) or what you list for a Proxmox cluster, and lets you choose which ones are sold, with a customer-facing name, icon and login user. This page shows how to prepare images that work well with NexCMP: password login, live CPU and RAM scaling, a swap file, and a root disk that grows to the plan size. ## Requirements | Item | Requirement | |---|---| | CloudStack template builder | A Linux machine with `libguestfs-tools` (`virt-customize`, `virt-sparsify`, `guestfish`), `qemu-utils`, `curl` and about 5 GB of free disk. No KVM and no root are needed. Internet access for package installs (or build with `--offline`). | | Where the builder is | It ships with every release in `/opt/nexcmp/releases//share/templates/` on the panel server. You do not need to run it there: copy the folder to any Linux machine. | | Web server | Any HTTP(S) server that CloudStack's secondary storage VM can reach, to publish the finished `.qcow2` file. It does not have to be reachable from the Internet. | | CloudStack | An account that may register templates, and the ID of your zone and of a matching OS type. See [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/). | | Proxmox VE | Cloud-init template VMs with the QEMU guest agent, one per OS and per node. See [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). | | Depends on | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) or [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) connected first. | ## Prepare the infrastructure ### CloudStack: build a template Supported base images (the vendor image is downloaded and its checksum verified before anything is changed): | Image | `--distro` | Login user | |---|---|---| | Ubuntu 22.04 | `ubuntu-22.04` | `ubuntu` | | Ubuntu 24.04 | `ubuntu-24.04` | `ubuntu` | | Debian 12 | `debian-12` | `debian` | | Rocky Linux 9 | `rocky-9` | `rocky` | More distributions: add a line to `distros.conf` (image URL, checksum file, family `deb` or `rpm`). 1. Copy the builder to a build machine and run it: ```bash cp -r /opt/nexcmp/releases//share/templates ~/templates && cd ~/templates ./build-template.sh --distro ubuntu-24.04 --version v7 --work ./out ``` Add `--panel-url https://cloud.example.com` to bake in an optional first-boot monitoring agent that enrolls with your panel (HTTPS only). Without it, no agent is added. Other options: `--base ` to use an image you downloaded, `--offline` to skip package installs. 2. The result is `out/ubuntu-24.04-v7.qcow2` and its `.sha256`. The build ends by running `verify-image.sh`, which inspects the finished image read-only and refuses a bad one (it is kept as `.unverified`). You can run it again any time: `./verify-image.sh out/ubuntu-24.04-v7.qcow2 --no-agent`. What the builder puts in the image: | Item | Why | |---|---| | `qemu-guest-agent` | CloudStack can ask the VM about CPUs and IPs, and quiesce it for snapshots. | | cloud-init with the CloudStack data source, `set_passwords` on every boot, SSH password login | The generated password is fetched from the virtual router and **Reset password** works. | | A udev rule for hot-added CPU and RAM | Hot-added vCPUs and RAM come online by themselves, so live scaling and Auto Scaling work. | | A swap file service | Creates `/swapfile` of the smaller of 50% of RAM and 4 GiB after the disk has grown. `touch /etc/cmp-swap.disabled` turns it off. | | cloud-init `growpart` and `resizefs` | The small root disk grows to the plan's disk size on first boot. | | Empty machine-id, no SSH host keys | Every VM gets its own identity. | ::: warning Name the template ` ` NexCMP switches **Auto Scaling** on for templates whose name ends in `vN` or `vN.M` with **N of 6 or more** (`Ubuntu 24.04 v7`, `Rocky Linux 9 v7.1`). The builder enforces this for `--version`. Register the template with exactly the printed name. ::: 3. Publish the file and register it in CloudStack. Use the SHA-256 the build printed, and register only after the file is completely written: ```bash cmk list ostypes keyword="Ubuntu 24.04" filter=id,description cmk register template name="Ubuntu 24.04 v7" displaytext="Ubuntu 24.04 v7" \ url=https://files.example.com/ubuntu-24.04-v7.qcow2 zoneid= format=QCOW2 hypervisor=KVM \ ostypeid= passwordenabled=true sshkeyenabled=true isdynamicallyscalable=true \ requireshvm=true isextractable=false ispublic=true isfeatured=true \ checksum="{SHA-256}" \ details[0].guest.cpu.mode=host-passthrough details[0].rootDiskController=osdefault ``` - `passwordenabled=true` lets CloudStack generate the VM password. - `isdynamicallyscalable=true` allows live scaling. - `host-passthrough` is needed for current Rocky and Alma images to boot; `osdefault` keeps the disk controller portable. 4. Wait for the template to reach **Download Complete / Ready**. The vendor image keeps its small virtual size (about 3.5 GiB for Ubuntu and Debian, 10 GiB for Rocky). Remember that a plan's disk cannot be smaller than the template's disk. ### Proxmox VE: make a template Build a cloud-init VM template per OS on every node you deploy to, with the guest agent installed and root login enabled, then register it as `os-key=vmid`. The full commands are in [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). The CloudStack builder above is **not** for Proxmox: it configures cloud-init for the CloudStack data source. ### ISOs Customers mount their own ISOs through the **Images** module (CloudStack) or the Dedicated Server module. Those are separate from the OS templates on this page. See [Modules](https://nexcmp.com/docs/infrastructure/modules/) and [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/). ## Connect in NexCMP 1. Open **Services › Virtual Machines › Template**. Click **Sync now** or wait up to 15 minutes. The sync adds templates that are ready and featured in CloudStack. ![The Template page listing synced templates with their ready state and CPU hot-plug column](https://nexcmp.com/docs/images/infrastructure/templates-01-list.webp) 2. Click **Edit** on a new template and set what customers see: | Field | Example value | |---|---| | Display name for customers | `Ubuntu 24.04 LTS` | | OS family | `ubuntu` (icon and default user) | | Version | `24.04` | | Login username | `ubuntu` (Windows: `Administrator`; other: `root`) | | Icon | image URL, a path or a name | | Order | `10` | | Active | On | | vCPU/RAM hot-plug (live scale) | **Auto by name (v6 or later)**, **Yes** or **No (needs a restart)** | ![Edit template dialog with a customer-facing name, OS family, login user and the hot-plug option](https://nexcmp.com/docs/images/infrastructure/templates-02-edit.webp) 3. Switch the template on. A template that CloudStack no longer lists is hidden automatically (and you are alerted in Telegram if configured); it is never deleted from NexCMP. ### Replace a template with a newer build Use **More › Replace with new version** on the sold template. Pick the new, ready, currently disabled template. The sold entry then points at the new build and keeps its name, order and icon; the old one is disabled as a way back (to go back, use **Replace** on the new entry). Running VMs are not touched. If the new build is a different OS family, tick the confirmation first. ## Verify 1. The row shows **ready** and, for builds named `vN` with N of 6 or more, **CPU hot-plug: supported**. 2. Deploy one test VM from the template and check inside the VM: - you can log in with the password shown in the portal; - `df -h /` shows the plan's disk size; - after a live scale (**Change plan** with no reboot), `nproc` and `free -m` grow; - `swapon --show` lists `/swapfile` (disks with less than swap plus 2 GiB free skip it); - `systemctl is-active qemu-guest-agent` prints `active`. 3. If you baked the agent in, the VM appears in the panel's monitored hosts a minute after the first boot. The VM needs a public IP of its own: the panel recognizes it by its public source IP. ## Limits and notes - The builder is Linux-only and builds Linux images. Other images (for example Windows) can be registered in CloudStack by other means; NexCMP lists what CloudStack reports. - Only the amd64 agent is shipped; other architectures skip the agent install. - The image is checked with `guestfish` but never booted by the builder. Always deploy one test VM before selling a new image. - Skip the agent on one VM with `touch /etc/cmp-agent.disabled` before first boot. Remove it later with `systemctl disable --now cmp-agent cmp-agent-install; rm -rf /etc/cmp-agent /usr/local/bin/cmp-agent`, then delete the agent in the admin console. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `virt-customize` fails to download packages | The build machine has no outbound network | Fix DNS or proxy on that machine, or build with `--offline` on a base image that already has the packages. | | `libguestfs: error: /usr/bin/supermin exited with error status 1` | The host kernel is not readable by the current user (Ubuntu) | `sudo chmod +r /boot/vmlinuz*`, or run the build as root. | | Checksum differs, or the vendor image no longer exists | Vendors move their "latest" files | Download the image yourself, check it against the vendor's checksum file and pass it with `--base`. | | Rocky: a newer XFS cannot be mounted | The host kernel is too old | Build on a machine with a recent kernel. | | `--version must be v6 or higher` | The name must be `vN` with N of 6 or more | Use `--version v7`. | | `--panel-url must be a plain https:// URL` | The agent is downloaded and run as root, so plain HTTP is refused | Use an `https://` URL without spaces, quotes or a query string. | | Template stays **not ready** | CloudStack has not finished downloading | Wait for **Download Complete**, check the secondary storage VM can reach the URL. | | Row shows **no-pass** | The template was registered without `passwordenabled=true` | Register it again with that flag. | | CPU hot-plug shows **unsupported** | The name does not end in `vN` (N of 6 or more) | Rename the template, or set **vCPU/RAM hot-plug** to **Yes** in Edit if the image has the udev rule. | | "Empty template list" after the first connection | The sync has not run | Click **Sync now** (needs the worker running) or wait for the 15-minute cycle. | --- # Dedicated servers (bare metal) > **Where in the admin:** Infrastructure › Bare Metal (Servers `/infra/baremetal`, IP Pools, Stock / iDRAC import, Operations, Templates, Customer ISOs, Safety). Plans and subscriptions: Services › Dedicated Server (`/dedicated`). Turn the module on under System › Admin › License & updates (`/license`). ## What it does NexCMP manages physical servers **directly through their iDRAC**, over the Redfish API. Customers rent a whole server: they power it on and off, open a browser console, mount an ISO or have an operating system installed from a template, and see hardware and metrics. They never see the iDRAC address, user name or password. Use it to resell dedicated servers from your own racks. ## Requirements | Item | Requirement | |---|---| | Hardware | Dell servers with **iDRAC 9** and the Redfish API. | | Management network | **You provide it.** The panel server must reach every iDRAC on **HTTPS 443** (and the iDRAC VNC port, default **5901**, for the console), and every iDRAC must reach the ISO web server below. The iDRAC address must be an **IPv4 address in a private range** (RFC 1918, a prefix of /16 or smaller) that you list in the setting `dedicated_bmc_cidrs`. DNS names and IPv6 are refused. NexCMP does not create this network and cannot test it for you. | | iDRAC account | One account with **Administrator** privilege on each iDRAC. NexCMP uses it to read the server and to send the few commands it allows itself (power, virtual media, one-time boot, console). It never creates iDRAC accounts for your customers. | | Tools on the panel server | `xorriso` and `genisoimage` (build the install ISO of each server) and `novnc` (the browser console page). Install them with `sudo apt-get install -y xorriso genisoimage novnc`, or let the installer do it: run it with `--baremetal`, or give it the ISO server options; an update also installs them when the Bare Metal module is already on. If one is missing, **Safety** lists it in a red notice, **live ops** refuses to turn on (error `tools_missing`), and the console and OS install answer that a tool is missing instead of failing later. | | ISO web server | A plain **HTTP** server on the management network that serves the NexCMP ISO folder to the iDRACs. Its address is a private IPv4 address and an optional port, for example `http://10.20.0.2:8080`, **without a path**, stored in `dedicated_iso_base_url`. It must support byte ranges. The folder is written by the panel's worker, so the web server normally runs on the **panel server itself**, listening on an address that server has on the management network. NexCMP ships a ready nginx site and a one-step command for it (see below). | | ISO folders | A folder for ISO files and, inside it, `pub` (what the iDRACs read) and `server` (customer ISOs). The one-step command uses `/srv/nexcmp-iso`; without it the defaults follow the install (settings `dedicated_iso_lib_dir`, `dedicated_iso_pub_dir`, `dedicated_iso_server_dir`). The NexCMP worker must be able to **write** to them. It runs with a read-only file system except `/var/lib/nexcmp`, so a folder elsewhere needs a systemd drop-in (the one-step command installs it). | | Callback address | `dedicated_callback_base`, an `https://` that the installing server can reach and that points to your panel. | | Storage | Free disk for the ISO library and for customer-supplied ISOs. | | Depends on | The module **Bare Metal**, a zone, and the IP pools of the servers. | ::: warning Sample values are not a configuration A fresh install carries sample values in `dedicated_bmc_cidrs`, `dedicated_iso_base_url` and `dedicated_callback_base` (a private sample network and `https://example.com`). They are not your network. While any of them is still a sample value, the Bare Metal pages show a red **Management network not configured** notice, importing an iDRAC fails the allow-list check, and the **live ops** switch refuses to turn on (error `config_placeholder`). Set the three values under **Infrastructure › Bare Metal › Safety › Management network connection** (see below). ::: ## Prepare the infrastructure 1. **Put every iDRAC on a management network** that customers cannot reach, and give each one a fixed private IPv4 address. Check the Redfish API from the panel server: ```bash curl -sk -u 'nexcmp-admin:XXXX' https://10.20.0.11/redfish/v1/Systems | head ``` 2. **Create an administrator account on the iDRAC** for NexCMP (iDRAC web UI, or `racadm`). Use a strong, unique password. Never reuse the iDRAC's built-in account. 3. **Publish the ISO folder to the iDRACs - one command.** On the panel server, as root, with the address that server has on the management network and the iDRAC network: ```bash sudo nexcmp dedicated-setup-iso --listen 10.20.0.2:8080 --allow 10.20.0.0/24 ``` `--listen` must be an address of this server (private IPv4, port 8080 if omitted). `--allow` lists the iDRAC network(s) (private CIDRs, /16 or smaller, up to 8, comma separated); only they can download, and the command also uses them as `dedicated_bmc_cidrs` when that setting is still the sample value. `--dir /path` picks another ISO folder (default `/srv/nexcmp-iso`). The command: - creates the folder with `pub` and `server` inside, owned by the NexCMP service user; - creates the group `nexcmp-iso` and puts the nginx user in it, so nginx can read an ISO while an iDRAC has it mounted and nobody else can; - installs the nginx site `nexcmp-iso` (kept only if `nginx -t` accepts it, nginx is installed if missing) and a systemd drop-in `nexcmp-vmd.service.d/iso.conf` (`ReadWritePaths` for the folder, `SupplementaryGroups=nexcmp-iso`); - writes the ISO settings (`dedicated_iso_base_url`, the three folders, the group) and restarts the NexCMP worker. It can be run again with other values, and `sudo nexcmp dedicated-setup-iso --remove` takes the nginx site and the drop-in away again (ISO files stay). It can also run at install time: add `--iso-listen`, `--iso-allow` (and `--iso-dir`) to the install command. The command does **not** create the management network and does not contact any iDRAC; if the values are wrong, the address is not on this server or the port is taken, it stops with a message before changing anything, and an nginx site that `nginx -t` refuses is not kept. 4. **What the ISO web server serves.** The nginx site answers `GET` (and `HEAD`, with byte ranges) for `/pub/.iso` and nothing else: no directory listing, no other path under the folder (the library and `server/` are not reachable), no other method, and only from the `--allow` networks (anyone else gets 403). NexCMP exposes each ISO an iDRAC has to read as a file or a short-lived link in `pub`; nginx follows a link only when the link and its target have the same owner (the service user), so a link to a system file is refused. Check it from another host on the management network: ```bash curl -I http://10.20.0.2:8080/ # 403 or 404, never a listing curl -I http://10.20.0.2:8080/pub/ # 403 or 404 ``` A test file in `pub` should answer a `Range` request with 206. *Doing it by hand instead:* the nginx site and the drop-in are shipped as templates in the release package (`share/dedicated/nginx-iso.conf` and `share/dedicated/vmd-iso.conf` under `/opt/nexcmp/releases//`). Copy them, replace the `__...__` markers (listen address, folder, allow lines, group) and keep the rules above. 5. **Have the OS images ready.** Place self-installing ISOs in the library folder (see **Templates** below). Customers can also add ISOs from a link; those are downloaded to the server, never given to the iDRAC as a URL. ## Connect in NexCMP ### 1. Turn the module on On **System › Admin › License & updates**, switch **Bare Metal** on. The **Bare Metal** category appears under Infrastructure. See [Modules](https://nexcmp.com/docs/infrastructure/modules/). ### 2. Set the management network connection Open **Infrastructure › Bare Metal › Safety**. The card **Management network connection** holds the three values that decide whether anything can work: | Field | Setting | What to enter | |---|---|---| | iDRAC network (CIDR) | `dedicated_bmc_cidrs` | The private network(s) of your iDRACs, for example `10.20.0.0/24`. Private CIDRs only, /16 or smaller, up to 8, comma separated. Only iDRACs in these ranges can be imported. | | ISO server for the iDRACs | `dedicated_iso_base_url` | `http://[:port]`, no path: the address of the ISO web server from the previous section, for example `http://10.20.0.2:8080`. The one-step command fills it for you. | | Panel callback address | `dedicated_callback_base` | `https://`, no path: the address the server being installed calls to report completion. It must be reachable from that server's network. `example.com`, `localhost` and similar are refused. | The form checks the format as you type and the server checks it again on save; a wrong value is refused with the reason and the stored value stays as it was. A field that still holds the shipped sample value carries a red **Not configured** label, and every Bare Metal page shows the red notice **Management network not configured** until all three are real. Changes are written to the audit log with your reason. Saving the sample value again does not count as configuring it unless you typed it yourself. ### 3. Check the safety gate (leave it closed for now) Open **Infrastructure › Bare Metal › Safety**. **Live ops** is the system-wide switch for every write command to an iDRAC (power, virtual media, console and OS install). It starts **off**, and it cannot be turned on while the management network connection above still holds sample values (the switch answers `config_placeholder` and names the fields). Writes are refused at the API and again inside the worker, while reading status keeps working. A second lock, **ops_locked**, protects each server: newly imported servers are always locked, even when live ops is on. ![The Safety page with Live ops off and the list of blocked actions](https://nexcmp.com/docs/images/infrastructure/dedicated-01-safety.webp) ### 4. Import servers 1. Open **Infrastructure › Bare Metal › Stock / iDRAC import**. 2. Under **Add one server** enter the iDRAC address, account and password, then click **Test connection**. The test only sends read commands and saves nothing. | Field | Example value | |---|---| | iDRAC address (URL or host) | `10.20.0.11` | | Account | `nexcmp-admin` | | Password | `XXXX` | | Label | `rack1-01` (empty: generated) | | Assign an IP from a pool | optional; choose the server's service IP pool | | Include in the sales list | off until you are ready | ![Stock and iDRAC import with an example address and a successful read-only test](https://nexcmp.com/docs/images/infrastructure/dedicated-02-import-server.webp) 3. Click **Save to stock**. NexCMP reads the model, CPU, RAM, disks, NICs and service tag from the iDRAC. The server enters stock **locked**. 4. For many servers use **Bulk import**: one server per line, fields separated by comma, semicolon or Tab. **Preview** checks the allow-list, duplicates of the iDRAC address or service tag, and reads the hardware (up to four servers at a time). ### 5. Customer ISOs and the ISO web address Open **Infrastructure › Bare Metal › Customer ISOs**. - **HTTP address serving ISOs to iDRAC** is `dedicated_iso_base_url`, the same value as in the Safety card (editable in either place). Enter the management-network address of your ISO web server. - **Customer ISO limits** set the size of one ISO, the total ISO storage, the minimum free space, how long ISOs are kept (hours), the maximum download time and the number of concurrent downloads. ![Customer ISOs with the HTTP address for the iDRACs and the ISO limits](https://nexcmp.com/docs/images/infrastructure/dedicated-03-iso-settings.webp) ### 6. OS templates Open **Infrastructure › Bare Metal › Templates**. It lists self-installing ISOs found in the library folder. Click **Register** on a file to fill in OS family, version and account (pre-filled from the file name), then switch the template **Enable**. Enabling a template starts nothing: every install still passes the safety gate. **Unregister** only removes the template row, never the file. ![Bare Metal Templates with a registered and an unregistered ISO](https://nexcmp.com/docs/images/infrastructure/dedicated-04-os-templates.webp) ### 7. IP pools Open **Infrastructure › Bare Metal › IP Pools** and add the IPv4 ranges that servers receive as service addresses. Each server gets one address; **Assign an IP from a pool** on import takes the next free one. Free addresses are counted per pool. ![Bare Metal IP Pools with example ranges and free counts](https://nexcmp.com/docs/images/infrastructure/dedicated-05-ip-pools.webp) ### 8. Plans and sale 1. Open **Services › Dedicated Server › Plans & pricing**. Edit the four sample plans (named `BM-40C-` in 1.6.1) or create your own: price, cycles and the set-up fee. **On a new install prices are entered in US dollars, to the cent, and the wallet is charged exactly that: no exchange rate is needed.** An install that already had plans priced in Vietnamese dong keeps dong and still needs an exchange rate in its settings (without one, orders are refused with `dedicated_fx_unavailable`). The sample plans start with a price of 0 and **disabled**: a plan on sale needs a price above 0. Check each plan against the hardware you actually import; a sample plan name does not detect or change a server's hardware. 2. Open the server (Infrastructure › Bare Metal › the server › **Sales**), **attach the plan** and turn **Sellable** on. 3. Turn **live ops** on (Safety page, with the typed confirmation) and unlock the servers you want to operate (the server's **Lock** tab, with a reason that goes into the audit log). 4. On **Services › Dedicated Server**, turn on sales. This takes two steps: read the consequences, then type `ENABLE SALES`. Until then customers see no order page and are not charged. ![Dedicated Server plans with the sales switch off](https://nexcmp.com/docs/images/infrastructure/dedicated-06-plans.webp) ### What customers can do on their server page The server page of a customer has sixteen tabs in seven groups (Overview, Control, Network, Hardware, Billing, Activity, Settings). The **Billing** group holds: - **Plan & renewal**: pay 1 to 24 months ahead in one step; the new date is counted from the current renewal date and the wallet is charged once. Auto-renew can be switched on or off. - **Billing cycle**: choose another cycle (for example quarterly); it applies from the **next** renewal, and nothing is charged or refunded now. - **Change plan**: a customer can **ask** for another plan. Nothing is charged by the request. You approve or reject it under **Services › Dedicated Server › Plan changes**, after you have upgraded the hardware or moved the server. An approved upgrade charges the difference for the rest of the period the customer has already paid; a downgrade refunds nothing and applies from the next period. - **Invoices**: every invoice of that server (order, renewals, upgrades). On **Plan changes**, sort requests by ID, date, state, plans or server; sorting covers the whole result on the server. The action column stays visible when you scroll horizontally on a narrow screen. At the top of the **Overview** four live tiles show **CPU**, **RAM**, **Disk** and **Network**, with a small chart for the last 24 hours. CPU, RAM and Disk are usage percentages from the last iDRAC poll (Disk is I/O activity, not free space); Network is the current inbound and outbound speed plus the month total, taken from the traffic counter. A tile shows **—** and a short reason when the server is off, the iDRAC cannot be reached, or the machine does not report that value; it never shows a made-up 0. These readings come from Dell iDRAC, and without a traffic counter the Network tile stays at **—**. The tiles refresh every minute while the page is open, and a click opens the **Hardware** or **Bandwidth** tab. On the **Overview** the **Access** card shows the user name and the password of the operating system you installed, hidden by default with **Show** and **Copy** buttons. It is available after every install, not only once, and a new owner of the server never sees an earlier owner's password. If the OS password is changed inside the server, the card still shows the one set at install time. The **Bandwidth** tab only appears when a traffic counter feeds it (see Limits). There is no **Subnet** tab: bringing your own IP prefix to a rented server is not part of NexCMP. ### What customers can and cannot do | Customers can | Never possible | |---|---| | See inventory, health, power state and metrics | See the iDRAC address, user name or password | | Power on, graceful shutdown, restart, force off, reset | Change iDRAC accounts or iDRAC network settings | | Open a browser console (one session at a time) | Change BIOS or RAID, clear logs, update firmware, reset the iDRAC | | Mount an ISO or an own ISO link, set boot once, reinstall the OS from an enabled template | Run raw Redfish calls (the functions do not exist in NexCMP) | NexCMP does not create iDRAC accounts for customers: they use the controls of the portal only, and the iDRAC address and login are never shown to them. ## Verify 0. On **Safety**, the **Management network connection** card shows no red label and the Bare Metal pages show no red notice. From a host on the management network, `curl -I http:///` does not list files. 1. **Test connection** on the import form returns the model, CPU, RAM and service tag. 2. The server appears in **Servers** with its hardware. With **live ops off**, a power action is refused with "Server control is temporarily disabled system-wide". 3. After turning live ops on and unlocking one test server, power the server off and on from **Operations** or the server page, and open the console. 4. Place a test order with a test plan: the server is assigned, the OS installs, and the install ends with the server reporting back to the panel. ## Limits and notes - Every physical server has at most one live subscription. - A write command is sent only when **both** live ops is on **and** the server's lock is open. The check is repeated inside the worker immediately before each command. - The **Installing** state waits for the new OS to answer on its port (SSH 22, Windows 3389, Proxmox 8006) before the order completes; an install has a time limit (`dedicated_install_timeout_min`, default 150 minutes). - Bandwidth for dedicated servers is counted from a traffic-counter agent that is not part of the standard installation. Without it nothing is recorded, the portal hides the Bandwidth tab and the API reports `capabilities.bandwidth` as `false`. - Admin actions on a rental (lock, unlock, cancel with or without a refund) are the same unified actions as for every other service, with refunds returned to the balance they were paid from. - Servers are not resold across resellers: Dedicated is sold from the main site only. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Test fails with an address error | The address is not an IPv4 literal in `dedicated_bmc_cidrs` (private, /16 or smaller) or the port is not allowed | Use the iDRAC's IPv4 address and add its network under Safety › Management network connection (`dedicated_bmc_cidrs`). DNS names and IPv6 are refused. | | Red notice "Management network not configured", or the live ops switch answers `config_placeholder` | One of `dedicated_bmc_cidrs`, `dedicated_iso_base_url`, `dedicated_callback_base` still holds the shipped sample value | Enter the real values under Safety › Management network connection. | | A field is refused when saving | Wrong format (CIDR not private or wider than /16, ISO address not `http://[:port]`, callback not `https://`), or a sample address such as `example.com` | Read the message under the field; the stored value is unchanged. | | `nexcmp dedicated-setup-iso` stops before changing anything | `--listen` is not an address of this server, the port is already used, or a value is not valid | Use the address this server has on the management network, another port, or correct the value named in the message. | | "The iDRAC TLS certificate differs from the pinned one - polling and write actions have stopped" | The iDRAC certificate was replaced (new certificate, replaced iDRAC) | Open the server, tab **Access**, compare the two fingerprints and, if the change is intended, click **Repin certificate**. | | "Server control is temporarily disabled system-wide" | Live ops is off | Turn it on in **Safety**. | | "This server is locked for operations" | The per-server lock is closed | Open the lock in the server's **Lock** tab with a reason. | | "Another operation is still running on this server" | A previous operation is not finished | Wait; check **Operations**. | | "Another console session is already open on this server" | One console at a time | Close the other session. | | "The server's controller is temporarily unavailable" | The iDRAC does not answer | Check the management network and the iDRAC. | | An ISO mount fails with a 403 or 404 from the iDRAC | The web server cannot read the file or the base address is wrong | Check the ISO web server's permissions and the **HTTP address serving ISOs to iDRAC**. | | "This plan is out of stock in the selected location" | No sellable server with that plan in the zone | Attach the plan to a server and turn **Sellable** on. | | "No IPv4 address is available to assign right now" | The IP pool is empty | Add addresses to the pool. | | "Automatic OS installation is not available right now" | Install prerequisites missing | Check that a template is enabled and the ISO folders are writable; customers can still install through ISO and console. | | Installation stalls | The installing server cannot reach the callback address | Check `dedicated_callback_base` and that the server's network can reach your panel over HTTPS. | --- # VPN and IPv6 proxy nodes > **Where in the admin:** Infrastructure › VPN & Proxy nodes › Agents & external nodes (`/agents`), VPN IP pools (`/vpn/pool`), IPv6 Proxy nodes (`/vpn/proxy6/nodes`). The services themselves are under Services › VPN and Services › IPv6 Proxy. Both modules are switched on in System › Admin › License & updates. ## What it does NexCMP sells VPN and proxy access that exits from **IP addresses you own**, on servers you operate. Two kinds of node exist: - **VPN node.** A small Go program, `cmp-agent`, runs on your Linux host. It discovers the public IPv4 addresses on the host, opens a SOCKS5, an HTTP and a UDP listener on each address that is sold, and sends traffic out from the **same IP** the customer connected to (same-IP egress). It reports to the panel every few seconds. - **IPv6 proxy node.** A proxy service that gives customers addresses from an IPv6 prefix you route to the host. The panel installs it for you over SSH. Use them to resell clean IP addresses in the countries where you have servers. ## Requirements ### VPN node | Item | Requirement | |---|---| | Operating system | A Linux distribution with systemd and a kernel of 4.15 or newer (IPv6 free-bind support). The shipped agent binary is **x86_64** (amd64) only. | | Privileges | root, for the install. The service keeps only `CAP_NET_ADMIN`, `CAP_NET_RAW` and `CAP_DAC_READ_SEARCH`. | | Tools | `curl` or `wget`, and `sha256sum` (the installer refuses to continue without checking the file). | | Public addresses | Public IPv4 addresses assigned on an interface of the host (one `/32` each), **or** a prefix routed to the host that you add as an extra subnet (IPv4 /16 to /32, IPv6 /48 to /128). Private, CGNAT and documentation ranges are ignored. | | Network, inbound | Customers connect to each sold public IP on **ports 65001-65535** (TCP for SOCKS5 and HTTP, UDP for UDP associate). Each endpoint uses a set of three consecutive ports. | | Network, outbound | HTTPS (443) to your panel's domain. The agent also looks up its own country on `ip-api.com` (HTTP) and `ipinfo.io` (HTTPS), with a 3.5-second limit. | | Mail | Outbound connections to port 25 are always blocked; there is no setting to open it. | | Docker, VPN and virtual interfaces | Interfaces named `docker*`, `veth*`, `br-*`, `cni*`, `flannel*`, `virbr*`, `cali*`, `tun*`, `tap*`, `wg*` and `tailscale*` do not contribute addresses. | | Depends on | The module **VPN**, a zone (created automatically from the host's country) and the NexCMP panel reachable over HTTPS. | ### IPv6 proxy node | Item | Requirement | |---|---| | Operating system | Linux with systemd on **x86_64**. The commands `systemctl`, `nft`, `ip`, `ss`, `sysctl` and `useradd` must exist; otherwise the install stops and names the missing command. | | Access for the install | A public IPv4 address (private, loopback and CGNAT addresses are refused), the SSH port, an account with root or `sudo` rights and its **password**. The password is used for the install only and is not stored. | | Free ports | **80, 443 and every port above 60000** must be free. The installer never stops or changes services that are already running; it reports the busy ports and stops. | | IPv6 | A public prefix between **/32 and /64** inside `2000::/3` that the host can use as a source for outbound traffic, delivered either **routed** to the host or **on-link** with NDP proxy (see below). | | Network | Customers connect to the node's IPv4 on port 80 (HTTP proxy), port 443 (SOCKS5) and ports 60001-65535 (one port per static IP). The node has no certificate, so there is no HTTPS proxy. Traffic leaves over IPv6 only, and only to destinations that have an IPv6 address. The node pulls its configuration from the panel over HTTPS. | | Node software on the panel server | The node daemon and its network script ship in the NexCMP release package. The panel copies them from its install folder (`/opt/nexcmp/releases//bin`, or `/opt/nexcmp/bin` on a custom build) to each node during the install. The panel server also needs `sshpass` and an SSH client; the installer adds `sshpass`, and on a server installed before 1.4.2 you add it with `sudo apt-get install -y sshpass`. | | Depends on | The module **IPv6 Proxy**, and the panel URL that nodes call back to (**IPv6 Proxy › Settings › Panel URL for nodes**, default `https://`). | ## Prepare the infrastructure ### VPN node 1. **Provision the host** with a supported Linux and the public IPv4 addresses you want to sell, either configured on the network interface or routed to the host. 2. **If you sell a routed block**, plan to enter it as an **Extra subnet** when you add the node. The agent installs a route `local dev lo` so the host accepts traffic for every address in the block, then reports the addresses it finds. The agent also reads the host's real routing table and never sells or captures a **gateway** address: if the block contains the next-hop of one of the host's routes (for example the router address of an on-link block), the agent installs `local` routes only for the parts of the block around it, reports the gateway to the panel, and the panel keeps that address out of the pool and out of every allocation. It removes the routes it installed when the extra subnet changes or the agent stops. 3. **Open the firewall.** Allow inbound TCP and UDP **65001-65535** on the public addresses, and outbound HTTPS to the panel. 4. **Optional: per-IP byte counters.** Conntrack accounting (`net.netfilter.nf_conntrack_acct = 1`) lets the monitoring role count bytes per IP. The panel can switch it on by sending the agent an `acct` command, which also writes `/etc/sysctl.d/99-cmp-agent.conf`. The VPN role itself does not need it. 5. **Check the panel from the node:** ```bash curl -fsS -o /dev/null -w '%{http_code}\n' https://cloud.example.com/install.sh ``` ### IPv6 proxy node An IPv6 prefix reaches the node in one of two ways. The installer tests both with real outbound connections and keeps what works. | Kind | What it means | What you configure | |---|---|---| | **Routed** | Your upstream routes a prefix (for example a /48 or /56) **to the node's own address**. Outbound packets can use any address in the prefix as their source and replies come back to the node. | Ask your provider for a routed prefix whose next hop is the node. Nothing more on the node: the proxy service uses transparent sockets, a fixed policy-routing table and `nft`. | | **On-link** | The /64 sits on the same LAN as the node. Replies work only if the node answers neighbour discovery for each address in use (**NDP proxy**). | The service sets, on that interface only, `net.ipv6.conf..accept_ra=2`, `forwarding=1` and `proxy_ndp=1`, remembers the old values, restores them when it stops, and blocks forwarding of the pool's addresses so the node does not become a router for the pool. | How the check works: the node tries a TCP connection to a public IPv6 address (port 443) from a random address of the candidate prefix, within 6 seconds. Candidates are global addresses on interfaces with a prefix of /64 or shorter, plus routes of type `local`, `unreachable` or `blackhole` between /16 and /64. A prefix that is shorter than /64 is accepted as **routed** if a random /64 inside it works. ::: warning Do not use the same prefix twice A prefix may not overlap a prefix of another node, and may not be wider than a prefix that already belongs to the panel's own IPv6 pool. A /64 **inside** the panel's pool is allowed. Overlaps are reported as "assign range … (node …)" in the install result, and the prefix is skipped. ::: Restrictions the node applies by itself: customers cannot send mail (ports 25, 465, 587, 2525) from the pool addresses, and the daemon's own IPv4 traffic is limited to replies and to the panel and DNS. ## Connect in NexCMP ### VPN node 1. On **System › Admin › License & updates**, switch **VPN** on. See [Modules](https://nexcmp.com/docs/infrastructure/modules/). ![The Module card with the VPN and IPv6 Proxy switches](https://nexcmp.com/docs/images/infrastructure/vpn-proxy-nodes-01-modules.webp) 2. Open **Infrastructure › VPN & Proxy nodes › Agents & external nodes**. You have two ways to add a node. **a. Install over SSH.** Click **+ Add server (SSH)** and fill in: | Field | Example value | |---|---| | Server IPv4 address | `192.0.2.50` | | SSH port | `22` | | SSH user | `root` | | SSH password | `XXXX` | | Display name | `node-ny-1` (empty: named after the IP) | | Server role | External VPN / Proxy Node | | Zone assignment | Auto by GeoIP (create / assign Zone), or an existing zone | | Extra subnet (optional) | empty to auto-scan the interfaces, or a routed block such as `203.0.113.0/24` | | Enable for sale after install | on to mark every detected address sellable | Click **Connect & install**. The panel connects over SSH, runs the one-line installer on the node with a one-time ticket, and shows the node name, GeoIP location and detected ranges. ![Add Node via SSH dialog with an example server address and role](https://nexcmp.com/docs/images/infrastructure/vpn-proxy-nodes-02-add-server-ssh.webp) **b. Install by hand.** Click **Generate manual install token**. The command appears **once**; copy it and run it on the node: ```bash curl -fsSL https://cloud.example.com/install.sh | sudo sh -s -- --token XXXX --name node-ny-1 ``` A token install enrolls the machine for monitoring only: for safety the panel does not let a machine that holds the shared token give itself the VPN role (adding `--roles vpn` to the command has no effect). After the machine appears in the list, open it and add the **VPN** role yourself. Installing over SSH (option a) gives the node the VPN role from the start. A new token cancels the old one; hosts that already enrolled keep working because they use their own key. ![Install command dialog showing the one-time token command](https://nexcmp.com/docs/images/infrastructure/vpn-proxy-nodes-03-install-command.webp) What the installer does: it downloads `cmp-agent-linux-amd64` from your panel, **verifies its SHA-256**, installs it to `/usr/local/bin/cmp-agent`, enrolls with the token, writes `/etc/cmp-agent/agent.json`, and starts the systemd service `cmp-agent`. To remove it: `curl -fsSL https://cloud.example.com/install.sh | sudo sh -s -- --uninstall`. 3. After the first heartbeat the node appears in the list. Its country is detected by GeoIP (the node's own lookup first, then the panel's database); a **zone** for that country is created and made public if none exists, and the addresses found on the host become pool ranges. 4. Open **Infrastructure › VPN & Proxy nodes › VPN IP pools** to see the ranges, their zone and node, and to change the zone, node or **Sellable** switch of each range. **Enable range** allows new orders to use its free IPs; **Disable range** only stops new allocations, leased IPs keep running. ![VPN IP pools with ranges reported by a node](https://nexcmp.com/docs/images/infrastructure/vpn-proxy-nodes-04-vpn-pool.webp) 5. Open **Services › VPN**, set the price list and turn sales on. The **Sell VPN** switch on **Subscriptions** is the same switch as the VPN module in License & updates. 6. **Optional: rename the service.** In **Settings › Branding**, **VPN service display name** changes how the VPN service is named in the customer menu, order page, detail page and deploy list (for example `IPv4 Proxy`, `Datacenter Proxy` or `Static Residential Proxy`; up to 40 characters, empty keeps `VPN`). It changes labels only: routes, data and the API do not change. ### IPv6 proxy node 1. On **System › Admin › License & updates**, switch **IPv6 Proxy** on. 2. Open **Infrastructure › VPN & Proxy nodes › IPv6 Proxy nodes** and click **Add node (SSH)**. Enter the node's IPv4 address, SSH port, user, password, an optional name, and a **Reason** (it goes to the audit log), then click **Install**. ![Add IPv6 node dialog with an example address and reason](https://nexcmp.com/docs/images/infrastructure/vpn-proxy-nodes-05-proxy6-add-node.webp) 3. The panel then, in order: checks the machine and the ports, copies the daemon, the network script, the service unit and a node-specific environment file (a node token and a password-signing key), runs the **probe** for usable prefixes, records the prefixes in the pool, starts the service, and waits up to 90 seconds for the first report. **Install log** shows each step. 4. A new node creates its zone in the **off** state (from the GeoIP of the node's IPv4). Turn the zone on under **Infrastructure › Datacenter › Zone** so customers can choose it. 5. Set a total **Mbps cap** per node (10 to 100000 per direction). Under **Services › IPv6 Proxy** set the price list and turn sales on. 6. Check **IPv6 Proxy › Settings › Panel URL for nodes**. Nodes call this address (`https://host`, no path) to pull their configuration and send reports. Empty means `https://`. It must be a host whose address points straight at the panel, so the node's own IPv4 reaches it unchanged. ::: tip Rotation schedule Customers can set an automatic rotation interval of 30 to 600 seconds. In NexCMP the panel runs that schedule (and the hourly clean-up of connection logs); nodes pick up a new address within about 5 seconds. ::: ### How a node reports | | VPN node (`cmp-agent`) | IPv6 proxy node | |---|---|---| | Enrollment | One-time ticket or token over HTTPS, then its own key | One-time SSH install, then a node token | | Heartbeat | Every **5 seconds** (10 s for a monitor-only agent); the panel can set 3 to 300 s. After a failure it backs off in steps of 5 s up to 60 s | The node sends a report about every 60 seconds and pulls configuration | | Shown as online | A heartbeat within the last **60 seconds** | **alive** within the last **120 seconds**, otherwise **missed heartbeat** | | Configuration change | Applied at the next heartbeat | Applied within about 5 seconds | | Updates | The panel can send an `upgrade` command with the new version, URL and SHA-256 | Reinstall from the node row | ### Data allowance on a VPN node From NexCMP **1.5.1** the VPN node agent sends the traffic it carried with every heartbeat, and the panel adds it to the plan's usage for the current period. The same number is shown to the customer (Overview and Bandwidth tabs) and to you (VPN › Subscriptions, column Used, and the node row). - **Cut-off.** When a plan has used its GB for the period, the panel stops sending that plan's endpoints to the agent, so new connections are refused and open ones are closed. The cut happens within one heartbeat, so a plan can run a little past its limit. Plans with an Unlimited tier are never cut. - **Open again.** The plan is served again within one heartbeat after the customer buys more bandwidth or when the next period starts. - **Not measured.** A node whose agent has not sent usage (for example an older agent) is shown as **Not measured**, and its plans are never cut because of GB. - **Update the agent.** Use agent **1.5.1** on the nodes. Older agents are still counted, but they can lose the bytes of a heartbeat that failed, of an endpoint that was stopped, or of a restart, and they do not close open connections when a plan is cut. Update from the node row (Reinstall) or with the `upgrade` command. - Counting is of the payload that passes through the proxy, so it is slightly lower (about 1 %) than the bytes on the wire. ### Retired VPN add-ons High IP Reputation, High Concurrency and High Priority Network are no longer offered on new orders. Existing subscriptions retain their add-on selections, but their renewal amounts have the retired fees removed. There is no refund of already-paid periods. The adjustment uses the add-on's current catalog price; if you changed that price or deleted the add-on, review the subscription's renewal amount before its next renewal. ## Verify 1. The node row is online. For a VPN node, **Agents & external nodes** shows its zone, country, role and detected ranges. 2. **VPN IP pools** lists the ranges with free IPs; the zone's detail page shows the VPN infrastructure. 3. Order a test VPN or proxy plan. The customer gets an endpoint `ip:port`, a user name and a password; connect through it and check that the visible public IP is the node's IP (for example `curl --proxy socks5h://USER:XXXX@IP:PORT https://ifconfig.me`). 4. On an IPv6 node, run on the host: ```bash systemctl is-active nft list tables | grep proxy6 ss -ltn | grep -E ':(80|443|6[0-9]{4})\b' ``` ## Limits and notes - Windows hosts can run the monitoring agent but not the VPN role. - Data allowance (GB per period) of a VPN plan: from NexCMP 1.5.1 the panel adds up the traffic reported by the VPN node agent and cuts the plan's proxies when the period's GB are used up (see "Data allowance on a VPN node" below). IPv6 Proxy plans have no GB allowance; they limit speed in Mbps per plan. - IPv4 addresses of a VPN plan do not rotate. A customer reports a blacklisted IP; you or the reseller approve a replacement from the same zone. Only IPv6 Proxy rotates. - At most 64 ranges are reported per heartbeat. - A VPN endpoint set uses three ports per sold IP. With ports 65001-65535 each IP serves a limited number of endpoints (178 port sets); the panel skips sets that were used before. - Deleting an IPv6 proxy node is allowed only when no plan holds an IP on it. It removes the node's ranges and token; the software on the host is not uninstalled. - Reselling a range that also carries your own VMs is possible: VPN uses only ports 65001-65535, so it shares the IP with other services. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "enrollment failed (invalid token or server unreachable)" | Wrong or already-used token, or the panel is unreachable | Generate a new token, or retry the SSH install. | | "file signature mismatch - STOP" | The downloaded agent differs from the published SHA-256 | Check the panel's `/dl/` folder and that nothing rewrites the download on the way. | | "this installer is for Linux only" / "architecture … is not supported yet" | Windows, or an architecture other than x86_64 (the release ships the agent for x86_64 only) | Use an x86_64 Linux host. | | Node never appears | The node cannot reach the panel over HTTPS | Test `curl https:///install.sh` from the node; check DNS and the firewall. | | Node appears with the wrong country | The country lookups failed, so the panel's address database was used | Allow outbound access to `ip-api.com` and `ipinfo.io`, or choose the zone explicitly in the SSH form. | | No ranges detected | No public IPv4 on a non-virtual interface | Add a public address, or enter the routed block as **Extra subnet**. | | VPN works, traffic exits from another IP | The sold IP is not assigned or routed to the host | Make sure the IP is on an interface or covered by `local … dev lo`. | | "the panel server does not have sshpass" | The panel host cannot log in to nodes with a password | Run `sudo apt-get install -y sshpass openssh-client` on the panel server, then install the node again. | | "not found in the installed package" when installing an IPv6 node | The panel is older than 1.4.2, or a custom build without the node files | Update NexCMP to 1.4.2 or later; for a custom build use `build.sh`, which now packs `brandy-proxy6d` and `brandy-proxy6-net`. | | "SSH: …" at the start of an IPv6 install, or "SSH to internal/loopback addresses is not allowed" | Wrong port, user or password, SSH blocked, or the address is private | Use the node's public IPv4 and check access manually with `ssh -p PORT USER@IP`. | | "architecture … not supported (x86_64 only)" | The node is not x86_64 | Use an x86_64 host. | | "node missing command: …" | `nft`, `ip`, `ss`, `sysctl`, `systemctl` or `useradd` not installed | Install the packages, then run the install again. | | "ports in use by another service on node: … — 80, 443, 60001-65535 must be free" | 80, 443 or a port above 60000 is busy | Move the other service; the installer does not stop it. | | "no IPv6 prefix with Internet access found from node (real egress probe failed)" | No routed prefix, NDP proxy not possible, or no outbound IPv6 | Check the prefix is routed to the node, the interface has IPv6 connectivity, and nothing filters outbound traffic. | | "all detected ranges already belong to another node" | The prefix is already used by another node | Remove it from the other node or pick another prefix. | | Install ends with "no initial report received from node" | The service started but did not reach the panel within 90 seconds | Check the panel URL given to nodes and the node's HTTPS path to it; see the **Install log**. | | Node row shows **missed heartbeat** | No report for 120 seconds | Check the service on the node, its clock and its path to the panel. | | Customers see no IPv6 location | The zone is off | Enable the zone under **Zone**. | --- # AI Models > **Where in the admin:** System › Admin › License & updates (`/license`) to turn the module on. Then Services › AI Models: Providers (`/ai/providers`), Models (`/ai/models`), Profit (`/ai/profit`) and Audit (`/ai/audit`). Customers use AI › API in the customer portal. ## What it does AI Models is a gateway and billing layer on your own panel. Your customers get API keys and call **one endpoint on your domain**, in the OpenAI-compatible or the Anthropic-compatible format. You choose which models to offer and what each costs per 1 million tokens. NexCMP checks the key, the limits and the wallet, sends the request to a provider you added, meters the tokens and charges the customer's prepaid wallet. **You bring the model sources.** NexCMP ships with no provider, no model, no default address and no price. It does not supply models, API keys, tokens or provider accounts. ::: warning Check your sources' terms first Many model sources restrict reselling or passing access on to third parties. Use only sources you are authorized to use and to resell, and follow their terms and the law that applies to you. This page is not legal advice. ::: AI Models is a separate product from **AI Chat and AI Agent** (module **AI**, see [AI providers](https://nexcmp.com/docs/ai/ai-providers/)). The two modules do not depend on each other. ## Requirements | Item | Requirement | |---|---| | Module | **AI Models** switched on under License & updates, Modules. It is off by default. | | Providers | One or more model sources you are authorized to resell, each with an OpenAI-compatible API (`/chat/completions`), an Anthropic-compatible API (`/messages`), or both. Each needs a base URL and an API key. | | Network | The panel must reach each provider over **HTTPS on port 443** at a public domain name. A provider on an internal host, another port or plain HTTP must be listed under **Allowed internal hosts** (setting `ai_endpoint_allow`). | | Customers | A customer account with a verified email address and a wallet balance. | | Hardware | None. NexCMP only forwards requests: no GPU and no model files. | ## Prepare the infrastructure 1. Get an API key from each model source you will use, and note its base URL (for example `https://api.example.com/v1`). 2. Check the source's terms on reselling. 3. If a source runs inside your network, decide its address, for example `http://10.0.0.5:8000/v1`. You will list `10.0.0.5:8000` under **Allowed internal hosts**. ## Connect in NexCMP ### 1. Turn the module on Open **System › Admin › License & updates**, find the card **Module**, and switch **AI Models** on. The menu reloads and **Services › AI Models** appears. While the module is off, the customer pages, the admin pages and the gateway all answer `404 module_disabled`, and no provider is contacted. ![The Module card with the AI Models switch on](https://nexcmp.com/docs/images/infrastructure/ai-models-01-modules.webp) ### 2. Add a provider 1. Open **AI Models › Providers** and choose **Add provider**. 2. Enter a short **slug**, a **name** (only you see it), the **type** (OpenAI-compatible, Anthropic-compatible or Both), the **base URL** and the **API key**, and a **priority** (a lower number is tried first). 3. Save. The key is stored encrypted and only its last four characters are shown afterwards. A key can be entered only from a signed-in administrator session. Use **Both** only for a gateway that really serves both formats. NexCMP does **not** translate between the two formats: a model served by an OpenAI-compatible provider is reached at `/ai/v1/chat/completions`, and one served by an Anthropic-compatible provider at `/ai/v1/messages`. ![The Providers page listing three sample providers with type, priority and status](https://nexcmp.com/docs/images/infrastructure/ai-models-02-providers.webp) ### 3. Test the connection Choose **Test connection** on the provider. NexCMP reads `GET /models` with the key and reports **ok**, or why it failed: **auth** (wrong key), **blocked**, **billing**, or **unreachable**. A test is limited to 6 per minute. ### 4. Sync the models Choose **Sync models**. NexCMP reads the provider's model list and adds each new model **switched off with no price**. A model the provider no longer lists is flagged. While the module is on, the list is also synced automatically every 6 hours (setting `ai_models_sync_hours`). ### 5. Set prices and switch models on 1. Open **AI Models › Models**. 2. For each model you want to sell, set the **selling price** in US dollars per 1 million tokens: **input**, **cached input** and **output**. 3. Optionally enter your **cost** for the model. Then **Profit** shows revenue against cost by day and by model, and a selling price below your cost plus the minimum margin (setting `ai_min_margin_pct`, 10 by default) is refused unless you confirm a deliberate loss. **Set price from cost** uses a markup that you choose; there is no default markup. 4. Switch the model on. A model with no selling price cannot be switched on. A cached input price left at zero is charged at the input price. ![The Models page with input, cached input and output selling prices and the on-off switches](https://nexcmp.com/docs/images/infrastructure/ai-models-03-models.webp) ### 6. Routes and failover On a model, **Routes** lists the providers that serve it, in order. Add a second provider as a fallback, set the order, and optionally give a different model name for each provider (the name sent to the provider can differ from the name you publish). When a provider returns a server error, an authorization or billing error, a missing model or a rate limit, NexCMP tries the next route. If a read times out after the request was sent, it is **not** sent to another provider, so you do not pay twice. Each provider is paced at `ai_provider_rpm` requests per minute (50 by default). If a provider keeps returning authorization or billing errors, NexCMP alerts the administrator on Telegram when Telegram is set up. ### 7. Open the sale switch At the top of the AI Models pages, the **sale switch** (setting `feature_ai_models`) is off by default. While it is off, customers see that the service is not open yet and the public price list reports it as not available. Check that at least one model is on, has a selling price and a route with a key, then switch it on. ### What customers can do In the customer portal, **AI › API**: - **Models** shows your models and prices, the base URL and ready-made example commands. - **API Keys**: create, rename, rotate, disable and delete keys. A secret is shown once; only a hash is stored. Per key: allowed models, expiry, a soft budget (warns) and a hard budget (blocks with `402`), requests per minute and tokens per minute. - **Usage** by model, key and day, with export. - **Logs**: filterable request records (time, key, model, tokens, cost, status, latency). **Prompts and responses are not recorded.** Logs are kept 90 days by default (`ai_log_retention_days`). - **Settings**: an account budget and the email address for budget alerts. - **Playground**: chat with a model, compare models side by side, and copy the matching code. ![The customer API Keys page with the base URLs for both API formats](https://nexcmp.com/docs/images/infrastructure/ai-models-04-keys.webp) The gateway endpoints, on your domain: ```text POST /ai/v1/chat/completions OpenAI-compatible POST /ai/v1/messages Anthropic-compatible (also /ai/v1/messages/count_tokens) GET /ai/v1/models model list GET /ai/v1/models/pricing public price list, no key needed ``` ### Wallet and billing AI usage is charged to the same prepaid wallet as every other service. Before a request goes to a provider, NexCMP reserves money for the most the request could cost; if the balance is below the minimum (`ai_min_balance_cents`, 20 cents by default) or too small, the request is refused with `402 insufficient_balance` and **no provider is called**. Usage is gathered per account and settled into wallet ledger entries after each hour, with a sweep every 5 minutes; usage not yet settled is already subtracted from the balance the gateway sees. Resellers can sell AI Models on their own domain: their markup or discount applies to your selling price and their wholesale wallet is charged the wholesale price. A per-reseller AI switch, on by default, controls who sells it. ## Verify 1. After step 3, **Test connection** shows **ok** and the model count. 2. After step 4 the new models are listed, off, with no price. 3. As a customer with a funded wallet, create a key and call the gateway: ```bash curl https://your-domain/ai/v1/chat/completions \ -H "Authorization: Bearer sk-ai-…" -H "Content-Type: application/json" \ -d '{"model":"model-one","messages":[{"role":"user","content":"hello"}]}' ``` A `200` response carries the model's answer. **Usage** shows the request, and **Profit** shows it if you entered a cost. 4. With the wallet at zero, the same call returns `402 insufficient_balance`. ## Limits and notes - The module has no provider, model or price built in, and no price list is ever read from a provider. - Tools that run on the provider side and are charged per call, such as web search, are rejected because they cannot be metered. Only a short list of request parameters is passed on. - Token usage is taken from the provider's reply; if a reply has none, NexCMP estimates it and marks the record as estimated. - Errors from a provider are cleaned before they reach a customer (names, hosts, addresses and the provider's model name are removed). Customers never see which provider served a request. - Customers reach the gateway through the domain of your panel, so that domain's TLS must be working. - NexCMP does not provide a service-level agreement, fine-tuning or GPU hosting for this module. ## Troubleshooting | Symptom | Cause and fix | |---|---| | Every AI Models page and the gateway answer `404 module_disabled` | The module is off. Switch **AI Models** on under License & updates, Modules. | | Adding a provider fails with `endpoint_host_not_allowed`, `endpoint_must_be_https` or `invalid_endpoint_port` | The base URL must be HTTPS on port 443 at a public domain name. For an internal host, another port or plain HTTP, list `host:port` under **Allowed internal hosts**. | | `blocked_endpoint` | The address is an IP, or the name resolves to a private address. List it under **Allowed internal hosts** if that is what you want. | | **Test connection** says `auth` | The key is wrong or has no access. Enter the key again. | | A model cannot be switched on (`no_price`) | Set a selling price first. | | Customers get `503 ai_unavailable` | The sale switch is off, or the reseller's AI switch is off. | | Customers get `503 upstream_unavailable` | No enabled route with a key serves that model in the requested format. Check the model's routes and the provider type. | | Customers get `402 insufficient_balance` | The wallet is below the minimum or too small for the request. Ask the customer to top up. | --- # Object Storage (S3, Ceph RGW) > **Where in the admin:** Infrastructure › Cloud provider › Ceph S3 (RGW) (`/infra/s3`). Accounts: Services › Object Storage › S3 accounts (`/s3`). Pricing: Services › Object Storage › PAYG pricing (`/s3/policy`). Switch the module on under System › Admin › License & updates. ## What it does Customers create S3 accounts from the portal and get access keys. NexCMP creates and deletes the matching users on **your Ceph RADOS Gateway (RGW)** through the RGW Admin Ops API, reads usage for billing, and bills pay-as-you-go: storage over a free tier per GB per month (charged by the hour) and download traffic per GB. Uploads are free. Use it when you run a Ceph cluster with RGW and want to resell object storage. ## Requirements | Item | Requirement | |---|---| | Ceph | A cluster with at least one RGW daemon, with the **Admin Ops API** enabled (the default `rgw_enable_apis` includes `admin`). | | Admin user | An RGW user for NexCMP with the capabilities listed below. Do not use a user that owns customer data. | | Endpoints | The S3 endpoint customers use, and the Admin Ops endpoint. Both are written as `https://host` with **no path, port 443 only**, and must resolve to a **public** address: loopback, link-local, multicast, private and unique-local addresses are refused. | | Certificate | A valid certificate for the endpoint host. | | Usage log | RGW usage logging on (`rgw_enable_usage_log = true`), so the usage API can report traffic. | | Zone | A master zone with the service **Object Storage (S3)** ticked. See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | Storage | Capacity in Ceph for the quotas you sell. | | Depends on | The module **Object Storage**. Download traffic also counts toward [Bandwidth](https://nexcmp.com/docs/catalog/bandwidth/). | ## Prepare the infrastructure On a Ceph admin node: 1. Create the admin user and give it the Admin Ops capabilities it needs: ```bash radosgw-admin user create --uid=nexcmp-admin --display-name="NexCMP admin" radosgw-admin caps add --uid=nexcmp-admin --caps="users=*;buckets=*;usage=read;metadata=read" radosgw-admin user info --uid=nexcmp-admin ``` Copy the `access_key` and `secret_key` from the output. They are the **Admin access key** and **Admin secret key** in NexCMP. 2. Turn on the usage log and restart the gateways: ```bash ceph config set client.rgw rgw_enable_usage_log true ``` 3. Put the gateways behind HTTPS on port 443 (a reverse proxy or the RGW's own TLS), publish the DNS name, and test from the panel server: ```bash curl -fsS -o /dev/null -w '%{http_code}\n' https://s3.example.com/ ``` You should get an S3 XML answer (HTTP 200 or 403), not a timeout. ## Connect in NexCMP 1. **Give a zone the S3 service.** Open **Infrastructure › Datacenter › Zone**, edit the zone and tick **Object Storage (S3)**. Only master zones with the S3 service appear in the backend form. 2. Open **Infrastructure › Cloud provider › Ceph S3 (RGW)** and click **Add backend**: | Field | Example value | |---|---| | Backend name | `Ceph RGW 1` | | Zone | the zone from step 1 | | RGW Admin Ops endpoint | `https://s3.example.com` | | Customer S3 endpoint | `https://s3.example.com` | | S3 signing region | `default` | | Admin access key | `XXXX` | | Admin secret key | `XXXX` | | Active | on | | Enable sales (requires connection test first) | **off** for now | | Reason for change | `initial setup` | ![Backend S3 dialog with example endpoints, region and masked keys](https://nexcmp.com/docs/images/infrastructure/s3-02-backend-dialog.webp) 3. Click **OK** (save), then **Check** in the row. NexCMP calls the Admin Ops API with the admin key. **Ceph connection successful** and the tag **Verified** mean it works. 4. Click **Edit**, switch **Enable sales** on and save. NexCMP refuses to open sales until a check has succeeded. 5. Open **Services › Object Storage › PAYG pricing** and set the pricing: | Field | Meaning | |---|---| | Storage over free tier (VND/GB/month) | Storage price; billed per hour as the monthly price divided by 730. | | Download (VND/GB) | Price for outgoing traffic. | | Upload (VND/GB) | Always 0. | | Free tier with Running VM (GB) | Fixed at 100 GB per account while the customer has at least one running VM, shared across their S3 accounts by size. | | Debt limit (VND) | How much unpaid usage is allowed. | | Billing threshold (VND) | Usage is collected into one invoice once it passes this amount. | | Min balance to create S3 (VND) | Wallet balance needed to create an account. | | Max S3 accounts per customer, Default technical quota (GB) | Limits for new accounts. | ![S3 PAYG pricing form](https://nexcmp.com/docs/images/infrastructure/s3-04-pricing.webp) S3 prices are entered in VND. They are converted for display and wallet charges at the exchange rate in effect; see [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). ## Verify 1. **Check** gives "Ceph connection successful". 2. As a customer (or with a test account), create an S3 account. On Ceph, `radosgw-admin user list` shows a new user, and **S3 accounts** lists it. 3. Create a bucket with any S3 client using the customer's keys against the customer endpoint. Upload a file. 4. After the next hour's billing tick, **S3 accounts** shows usage. ## Limits and notes - A backend that already has accounts attached keeps its **zone, endpoint and region**; to change them, create a new backend. - One zone can have its own backend; accounts and backends are tied to a zone for good. - The free tier and "uploads are free" are fixed rules, not options. - Keys are stored encrypted; the console shows nothing but a "leave blank to keep the current key" field. A key pair is always entered together. - Customers' secret keys are shown once at creation, never again. A lost key is replaced by rotating it. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Endpoint requires HTTPS" | The endpoint uses plain HTTP | Publish HTTPS on port 443. | | "Endpoint port not allowed" | A port other than 443 | Use the standard HTTPS port. | | "Endpoint address not allowed" | The name resolves to a private, loopback, link-local or unique-local address | Use a public address. | | "Endpoint must contain only protocol and host" | The URL has a path, query or user information | Use `https://host` only. | | "Failed to resolve endpoint" | DNS does not resolve from the panel server | Fix DNS. | | "Failed to connect to Ceph" | Firewall or TLS error | Test with `curl` from the panel server. | | "Ceph returned HTTP 403 AccessDenied" | Wrong keys or missing capabilities | Check the keys and re-run `radosgw-admin caps add`. | | "Invalid Ceph response" | The endpoint is not an RGW Admin Ops API | Point it to the RGW, not to a web page. | | "Connection test must succeed before enabling sales" | You switched on sales before a successful **Check** | Run **Check**, then enable sales. | | "All new keys for endpoint are required" | Endpoint changed without both keys | Enter the access key and the secret key together. | | "Backend already has attached resources; create a new backend to change zone or endpoint" | Accounts exist on this backend | Create a new backend. | | "Select master zone with S3 service" | The zone is not a master zone or has no S3 service | Tick **Object Storage (S3)** on the zone. | | "Invalid region" | The region has other characters than letters, digits, `-` and `_` | Use `default` or your RGW zonegroup name. | | "Ceph usage summary missing; not billed" | Usage log disabled | Enable `rgw_enable_usage_log`. | --- # Kubernetes and Private Cloud > **Where in the admin:** Kubernetes: Infrastructure › Cloud provider › CloudStack › Kubernetes (CKS) (`/infra/cloudstack?tab=k8s`), then Services › Kubernetes (Overview, Clusters, Versions, Policy). Private Cloud: Services › Private Cloud (Overview `/vpc`, VPC `/vpc/list`, Plans & pricing `/vpc/plans`). Switch both modules on under System › Admin › License & updates. ## What it does - **Kubernetes.** Customers create managed Kubernetes clusters on your CloudStack with CloudStack Kubernetes Service (CKS). The control plane is free; customers pay for **worker** nodes. NexCMP creates a separate network and public IP for each cluster and keeps each customer's clusters apart. - **Private Cloud.** Customers get a private network (a CloudStack VPC) with a router, subnets (tiers), firewall rules (network ACLs) and virtual machines inside it. It is sold by **bandwidth plan** with dedicated, unmetered speed (for example 50, 100, 500 Mbps, 1 and 3 Gbps). Each customer can have one VPC by default. Both are built on [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) and use its API through the same connection. ## Requirements ### Kubernetes | Item | Requirement | |---|---| | CloudStack | 4.23 (tested). CKS must be **enabled** (`cloud.kubernetes.service.enabled = true`). | | Account | A **Domain Admin** account in the dedicated domain, so NexCMP can create a CloudStack project for each customer and resource limits inside it. See [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/#create-a-dedicated-account-and-api-key). | | `endpoint.url` | The CloudStack global setting must point to your management API, reachable from the cluster VMs. NexCMP shows the value and the result of an endpoint check. | | Versions | At least one Kubernetes supported-version entry with its binaries ISO, in state **Enabled**. Versions that are past end of life cannot be put on sale. | | Control-plane offering | A free service offering with **exactly 2 vCPU, 2048 MB RAM and a CPU cap** (`limitcpuuse = true`), visible to the account. The server rejects anything else with `cp_offering_invalid`. | | Network | A CKS-capable isolated network offering that is enabled. | | IPs | Free public IPv4 addresses in the sellable range; each cluster uses one. | | Capacity | Each cluster costs you 2 to 4 GB of RAM for the control plane and a small router VM. Watch the capacity tabs. | ### Private Cloud | Item | Requirement | |---|---| | CloudStack | 4.23 (tested) with VPC enabled. | | VPC offering | One VPC offering. A dual-stack offering gives each tier an IPv6 /64 as well. | | Tier network offerings | **One network offering per bandwidth plan**, created for VPC, with the **network rate** (Mbps) equal to the plan, plus one **locked** offering of **1 Mbps** that is applied when a VPC is locked for non-payment. | | Account | The dedicated account (Domain Admin recommended). VPC networks stay in the account, not in projects. | | IPs | Free public IPv4 addresses (each VPC uses one for source NAT). | | Address space | Each VPC receives a /16 from `10.128.0.0/9`. Keep that range unused on your own networks. | | Depends on | CloudStack connected; the module **Private Cloud**. | ## Prepare the infrastructure ### Kubernetes: enable CKS and add versions 1. Enable the service and set the endpoint, then restart the management server: ```bash cmk update configuration name=cloud.kubernetes.service.enabled value=true cmk update configuration name=endpoint.url value=https://cloudstack.example.com/client/api sudo systemctl restart cloudstack-management ``` `endpoint.url` is called back by the cluster's controller, so it must be an address the cluster VMs can reach. 2. Build a Kubernetes binaries ISO for the version you want to sell, with the `create-kubernetes-binaries-iso.sh` script from the CloudStack source tree, host it on a web server and register it: ```bash cmk add kubernetessupportedversion name="Kubernetes 1.35.0" semanticversion=1.35.0 \ url=https://files.example.com/setup-v1.35.0-calico-amd64.iso zoneid= \ mincpunumber=2 minmemory=2048 cmk list kubernetessupportedversions filter=name,semanticversion,state,isostate ``` Wait for the ISO state to become **Ready** and the version to be **Enabled**. 3. Create the control-plane offering, limited to the NexCMP domain: ```bash cmk create serviceoffering name=K8S-CP-2-2 displaytext="Kubernetes control plane" \ cpunumber=2 cpuspeed=1000 memory=2048 limitcpuuse=true offerha=false domainid= ``` 4. Check that a CKS network offering is enabled: ```bash cmk list networkofferings state=Enabled forvpc=false filter=id,name ``` ### Private Cloud: create the offerings In the CloudStack UI (**Service Offerings › Network Offerings › Add network offering**) or with `cmk`: 1. List what you have: ```bash cmk list vpcofferings filter=id,name,state cmk list networkofferings forvpc=true filter=id,name,networkrate,state ``` 2. Use an enabled VPC offering (dual-stack if you want IPv6). If you need a new one, clone CloudStack's default VPC offering and enable it. 3. For **each** bandwidth plan you sell, create a network offering **for VPC** (clone the default "isolated network offering for VPC networks") and set **Network rate (Mb/s)** to the plan's speed: `50`, `100`, `500`, `1000`, `3000`. Enable each offering. 4. Create one more offering with **network rate 1** (`VPC-LOCKED-1M`) for locked VPCs: customers can still reach their servers over SSH, but the speed is limited. 5. Note the UUID of every offering. NexCMP needs them in the next section. ::: warning The shipped offering IDs do not exist in your cloud The Private Cloud form is pre-filled with offering UUIDs that belong to another CloudStack installation. Replace **every** one with your own, or the first VPC order fails. ::: ## Connect in NexCMP ### Kubernetes 1. Open **Infrastructure › Cloud provider › CloudStack** and choose the tab **Kubernetes (CKS)**. The right-hand card **CKS status on CloudStack (read-only)** shows `kubernetesserviceenabled`, `endpoint.url` with **Endpoint check**, the CKS network offering, the `cloud.kubernetes.*` settings and the list of ISOs and versions. Click **Recheck** after you fix something. 2. In **Sales config**, choose the **Control plane Offering** (a warning appears if it is not exactly 2 vCPU / 2048 MB / CPU cap), set **Max workers per cluster** (1 to 9: CKS allows 10 nodes including the control node), switch **Enable CKS for this backend** on and **Save**. The switch stays disabled until an offering is chosen. ![The Kubernetes (CKS) tab with the control plane offering chosen and the CKS status card](https://nexcmp.com/docs/images/infrastructure/kubernetes-and-private-cloud-01-cks.webp) 3. Open **Services › Kubernetes › Versions** and click **Sync from CloudStack**. Switch **Available for new clusters** on for the versions you sell, set the default and, if you like, an **End of life (EOL)** date. 4. Open **Services › Kubernetes › Policy**. In **Worker plans** add plans with a **Plan code** (A-Z, 0-9, hyphen, up to 16 characters). Set **NVMe per worker (GB)**, **Min and max workers** and the cluster limits (active clusters per account, creations per 24 hours, global cap, concurrent CKS operations). Then switch **Sell Kubernetes (CKS)** on. At least one plan must stay on sale. ![Kubernetes policy with worker plans and the sales switch](https://nexcmp.com/docs/images/infrastructure/kubernetes-and-private-cloud-03-policy.webp) ### Private Cloud 1. Open **Services › Private Cloud** (Overview). In the policy card replace **VPC offering** and **Locked offering tier** with your own UUIDs. Check the limits (VPCs per account, tiers, VMs and ACL rules per VPC, balance in hours of plan fee needed to create, plan changes per 24 hours, concurrent operations where 0 is an emergency stop) and the DNS servers. Click **Save** (a reason is asked). ![Private Cloud overview with the VPC offering and locked offering fields](https://nexcmp.com/docs/images/infrastructure/kubernetes-and-private-cloud-04-vpc-policy.webp) 2. Open **Services › Private Cloud › Plans & pricing**. For each plan click **Edit** and set the **VND price per month**, **On sale** and the **CloudStack tier offering** UUID whose network rate equals the plan's Mbps. The row shows cost and profit at full use, so you can see which plans are below your cost. ![Edit plan dialog for a Private Cloud bandwidth plan](https://nexcmp.com/docs/images/infrastructure/kubernetes-and-private-cloud-05-vpc-plan.webp) 3. Switch **Sell Private Cloud** on. To try it first, add test account IDs to **Beta accounts**: those accounts can use the service while the sales switch is off. ## Verify 1. Kubernetes: the status card shows `kubernetesserviceenabled: true`, a good endpoint check and an enabled version. Create a one-worker test cluster as a customer; it should reach running and `kubectl get nodes` should show it ready. Delete it afterwards: the network and IP go back to the pool. 2. Private Cloud: create a VPC with the smallest plan as a test customer. The VPC shows active, a tier gets its subnet, a VM in the tier reaches the Internet through the VPC router, and **Change plan** to a larger plan takes effect within about 20 seconds without dropping connections. ## Limits and notes - Kubernetes: workers are billed like VMs at the plan prices and count as VMs in the VM limits. A cluster can have at most 9 workers because of the CKS limit of 10 nodes. - Kubernetes: the control plane, router and public IP of a cluster are free to the customer; you carry their cost. - Private Cloud: all plans are **unmetered**. Prices in the form are Vietnamese dong per month; the cost column uses a reference price that is not yours, so use your own numbers. - A locked VPC is limited to 1 Mbps, is not billed and is not deleted automatically. - Private Cloud uses one router per VPC, not a redundant pair. If its host fails, CloudStack rebuilds it in a few minutes. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `cp_offering_invalid` / "This Offering is not 2 vCPU / 2048 MB / limitcpuuse" | The control-plane offering has other sizes or no CPU cap | Create the offering as in step 3 and choose it again. | | **Enable CKS** switch is disabled | No control-plane offering chosen | Choose one first. | | `kubernetesserviceenabled: false` | CKS is off in CloudStack | Enable it and restart the management server. | | Endpoint check fails | `endpoint.url` is not reachable from the cluster VMs | Set a reachable URL, restart the management server, click **Recheck**. | | "EOL: cannot enable for sale" | The version's end-of-life date has passed | Register a newer version. | | "No versions: click Sync from CloudStack" | No Kubernetes version registered | Register a version and sync. | | "CKS backend is OFF: customers cannot create clusters even if the sales flag is enabled" | The sales switch is on but the CKS backend is off | Enable CKS on the CloudStack tab. | | "At least 1 plan must be on sale" | Last worker plan removed | Keep at least one plan, or switch Kubernetes sales off. | | Cluster stuck creating | Not enough capacity or a missing ISO | Check the capacity tabs and the version's ISO state. | | A new VPC fails in the first step | The VPC offering or tier offering ID is not valid | Replace every offering UUID with one from your CloudStack. | | "cs_offering_id must be a UUID" | An offering ID is not a UUID | Paste the id from `cmk list networkofferings`. | | Plan change fails | The new tier offering does not exist or the network rate is wrong | Check the offering UUID and its network rate. | --- # Plans and pricing > **Where in the admin:** Services › Virtual Machines › Plans & pricing (`/vm/catalog`), with the tabs Plan, Custom plan, Cycle and Offering CloudStack. Templates are on the separate page Services › Virtual Machines › Template (`/vm/templates`). ## What it does A **plan** is a size customers can buy: vCPU, RAM, root disk and a price, in a zone, from one source (CloudStack or a Proxmox VE cluster). The customer chooses a plan, an operating system and a **billing cycle** (hourly, monthly or a longer prepaid term). Plans are sold in the customer portal, through the API and in resellers' sites. ## Requirements | Item | Requirement | |---|---| | Source | CloudStack connected (a plan points at a CloudStack service offering) or a Proxmox VE cluster attached to the zone. See [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) and [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). | | Zone | A zone that sells virtual machines. See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | Templates | At least one enabled, ready template. See [OS templates](https://nexcmp.com/docs/infrastructure/templates/). | | Exchange rate | Only if you enter prices in VND. See [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). | | Permissions | An administrator; every price change asks for a reason. | ## Prepare the infrastructure - **CloudStack:** create the service offerings you want to sell (public, or shared with the NexCMP domain). A plan points at one offering. The **Offering CloudStack** tab lists the offerings and disk offerings live from CloudStack (cached for 60 seconds). When the plans table is empty, the worker creates six plans from your real offerings automatically. - **Proxmox VE:** nothing in Proxmox; a plan carries its own CPU, RAM and disk. The disk must not be smaller than the template's disk. ## Connect in NexCMP ### How prices are stored NexCMP stores every price as **micro-dollars per hour** (µ$/hour; 1 µ$ is 1/1,000,000 USD). You can type the price in the unit you prefer and the form converts it: | Unit | Notes | |---|---| | USD / hour (6 decimals) | Stored as is. | | USD / month | Divided by the hours-per-month setting (730). | | VND / month | Converted once at the current exchange rate, then stored in µ$/hour. Appears only when a rate exists. | The form shows the result: µ$/hour, USD/hour, the monthly price and every billing cycle. **Monthly price = hourly price × 730**; a longer cycle is **monthly price × months × (1 − discount)**. ### Add a plan 1. Open **Services › Virtual Machines › Plans & pricing** and click **Add plan**. 2. Fill in the form: | Field | Example value | |---|---| | Plan name | `VM-2-4` | | Slug | empty: made from the name | | Zone | your default zone | | Infrastructure source | empty for CloudStack, or a Proxmox cluster | | Service offering CloudStack (live) | pick one (shown only for CloudStack) | | CPU (core), RAM (MB), Root disk (GB) | `2`, `4096`, `40` | | Disk offering | optional, for a custom root size | | Enter price by / Price | `USD / month`, `12` | | Short description | `2 vCPU, 4 GB RAM, 40 GB NVMe` | | Order | `10` | | Active | On: customers can buy | | Visibility | Visible in catalog | | Price change reason | `initial price` | 3. Click **OK**. The plan appears in the table with its hourly and monthly price and a cell for every enabled cycle. ![Plans table with hourly, monthly and per-cycle prices](https://nexcmp.com/docs/images/catalog/plans-02-list.webp) ### Billing cycles Open the **Cycle** tab to set which cycles customers can choose and their discounts. | Cycle | Default | Notes | |---|---|---| | Hourly | always on | Charged hour by hour. | | Monthly | on, 0% | | | 1 year | on, 10% | | | 2 years | on, 15% | | | 3 years | on, 20% | | You can rename a cycle, change its discount (0 to 90%) and switch it on or off. A change applies to VMs bought or renewed **after** you save; running VMs keep their locked price. ![Cycle tab with discounts per billing cycle](https://nexcmp.com/docs/images/catalog/plans-03-cycles.webp) ### Set a base plan and derive the others **Set base plan + derive other plans** reprices the whole table from one price. Choose the base plan and its price, then either keep the **current price ratios** between plans or compute from **component prices** (µ$/hour per vCPU, per GB RAM and per GB NVMe, with an optional bundle discount of 0 to 50%). A preview table shows old and new prices before anything is written. Click **Apply**, and give a reason. When you apply new prices: running **hourly** VMs get a **lower** price from the next billing hour, and prepaid VMs get it at renewal. If a price **goes up**, running VMs keep their old price. ### Custom plans The **Custom plan** tab lets customers choose their own vCPU, RAM and NVMe size. Set the hourly unit price for one vCPU, one GB of RAM and one GB of NVMe (leave empty to derive from a percentage discount), the minimum, maximum and step for each, and which billing cycles apply. A **custom** plan should cost more than a fixed plan of the same size, otherwise customers would skip the fixed plan; the page flags rows where custom is cheaper. Turn on **Enable custom plans for customers** to sell them; off shows "Coming soon" in the portal. Each custom VM gets its own hidden plan row; those rows cannot be edited or deleted while the VM exists. ### Change, hide, clone, delete | Action | What it does | |---|---| | **Edit** | Changes name, description, order, price, active state and visibility. | | **On/Off** | **Off**: customers cannot buy; running VMs are unaffected. | | **Hide / Show** | A hidden plan is not in the catalog but existing VMs keep it and an administrator can assign it. | | **Clone** | Creates a new plan in the same zone with a new source, size or price, optionally hiding the old one. To sell the same configuration from CloudStack and Proxmox, clone it and choose the other source. | | **Move up/down** | Changes the order. | | **Delete** | Removes the plan from sale and from the table. VMs that already use it keep it. | ::: warning Plans in use are locked Once a plan has VMs, its **zone, offering, CPU, RAM, disk and source** are locked so running VMs are not changed silently. Use **Clone** to create the new configuration; the old plan keeps its VMs. ::: ## Verify 1. The plan appears in the table with the expected hourly and monthly price. 2. In the customer portal, start an order: the zone offers the plan and the cycle prices match the **Cycle** tab. 3. Place a test order, check the wallet charge against the price shown, and delete the VM. ## Limits and notes - A plan belongs to **one** zone and **one** source. - Prices are stored in µ$/hour; the VND price shown is derived and rounded by the **VND rounding step** (see currency page). The wallet is always in USD. - Resellers get a wholesale and retail price derived from the list price; changing a price for an existing plan does not copy custom reseller prices to a clone whose configuration you changed. - Min, max and step limits of custom plans are checked against CloudStack's own limits; a maximum disk above CloudStack's `storage.max.volume.size` makes deploys fail and refund. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Plan name required" / "Service offering required" | Missing field | Fill the name; for CloudStack choose an offering. | | "CPU/RAM/disk must be > 0" | A size is empty or zero | Enter all three. | | "Invalid price (µ$/hr must be 1 … 100,000,000)" | The price is out of range | Check the unit and the amount. | | "Price change requires a reason" | Reason empty when editing | Type a reason (3 characters or more). | | "Offering "X" is 4c/8192 but plan specifies 2c/4096 - CloudStack will provision by offering. Save?" | The plan's numbers differ from the offering's | Make them match, or confirm to keep CloudStack's size. | | "Failed to fetch offerings from CloudStack" | The connection is down or the key lacks permission | See [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/). | | "Template is not ready on CloudStack - deployment will fail. Enable anyway?" | You enabled an unfinished template | Wait for the template to be ready. | | Plan cannot be edited ("has VMs - locked") | VMs use the plan | Clone it. | | The plan does not show for customers | Off, hidden, or the zone is not ready | Check **Active**, **Visibility** and the zone tag. See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | "Select at least 1 billing cycle" | No cycle ticked for custom plans | Tick at least one. | | "Bundle discount must be 0–50%" | Out of range | Use 0 to 50. | | VND price option missing | No exchange rate yet | Update the rate first. | --- # Bandwidth quotas and add-ons > **Where in the admin:** Services › Bandwidth: Summary (`/bandwidth`), Add-on (`/bandwidth/addons`) and Quotas, pricing & policy (`/bandwidth/policy`). The module switch is in System › Admin › License & updates. ## What it does Bandwidth gives every metered service a monthly traffic **quota**. NexCMP measures usage, warns the customer at 80% and 100%, and, when the quota is exceeded, acts according to your **overage policy**. Customers can buy **add-ons** of extra terabytes from their wallet; you can also grant add-ons for free. | Service | Quota | |---|---| | Virtual Machines | Per VM per month (default 2048 GB, that is 2 TB) | | Kubernetes | The VM quota multiplied by the number of workers | | Object Storage | Download traffic per customer per month (default 100 GB); beyond it the normal pay-as-you-go price applies | | VPN | Keeps the quota of its plan; upgrade bandwidth on the VPN page | | Private Cloud | **Unmetered** | ## Requirements | Item | Requirement | |---|---| | Module | **Bandwidth** is on by default. Turn it off under License & updates to hide it. | | Measurement source | With the default source, **CloudStack VM statistics**. Keep VM statistics collection enabled in CloudStack; the default retention of 720 minutes is enough. | | Proxmox VE | VMs on Proxmox VE are **not metered**. | | Exchange rate | The add-on price is entered in VND. See [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). | | Depends on | [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) for VM and Kubernetes traffic, [S3](https://nexcmp.com/docs/infrastructure/s3/) for storage downloads. | ## Prepare the infrastructure Not required beyond CloudStack being connected. NexCMP reads `listVirtualMachinesUsageHistory` for all VMs of the account in one call, every 5 minutes, in windows of at most two hours. Each sample covers one minute and is **added up**. Samples are taken three minutes in the past so they have time to arrive. A gap longer than CloudStack's retention (about 12 hours) is skipped and recorded. ::: info What the numbers mean Usage is counted on each VM's network interface, so it includes traffic between VMs, not only traffic to the Internet. It can be a little higher than an edge router would count. ::: ## Connect in NexCMP 1. Open **Services › Bandwidth › Quotas, pricing & policy**. 2. Set the values and click **Save**; a reason is asked and recorded. The worker applies the change within about 30 seconds. | Field | Default | Meaning | |---|---|---| | Enable Bandwidth (customer menu and buying add-ons) | on | The switch for the module's sales side. | | VM, GB/month (Kubernetes = this value × workers) | 2048 | Quota per VM per period. | | S3, GB download/month per customer | 100 | Beyond it normal S3 pay-as-you-go applies. | | Quota direction (VM, Kubernetes) | In + out | Or **Outbound only**. | | +1 TB price (VND) | 100,000 | Converted to cents at the quote rate. | | Max per purchase (TB) | 50 | | | Overage policy | suspend | **notify**, **throttle** or **suspend** (below). | | Enforce actual suspend from | migration date + 24 hours | Before this date nothing is suspended; customers are only warned. | | Throttle rate limit (Mbps) | | Only used by throttle. | ![Bandwidth policy: default quotas, add-on price and overage policy](https://nexcmp.com/docs/images/catalog/bandwidth-01-policy.webp) ### Overage policies | Policy | What happens when a service passes 100% | |---|---| | **notify** | The customer is notified. Nothing else. | | **throttle** | Like notify for now: the speed limiter is not connected in this release. | | **suspend** (default) | The VM, cluster or S3 account is suspended until the customer buys an add-on or the next period starts. Only effective from the **suspend-from** date. | The 80% and 100% alerts (bell and e-mail) always run, once per threshold, period and service. If you unlock a service that was suspended for bandwidth while it is still over quota, the next check (within 5 minutes) suspends it again. To keep it running, **grant an add-on**. ### Watch usage and grant add-ons Open **Services › Bandwidth › Summary**. It lists every service with its usage in the current period, quota including add-ons, remaining quota, and the 80% and 100% marks. KPI tiles show the number of services, add-ons sold in the last 30 days and the collector state (running, cursor). The **Daily** table shows the current period day by day. ![Bandwidth Summary with the collector state and usage per service](https://nexcmp.com/docs/images/catalog/bandwidth-02-summary.webp) To give a customer room, use **Grant** on a service (an add-on of whole terabytes at no charge). Open **Add-on** for the history of purchases and grants, newest first, with a type filter. ![Add-on history showing purchases and free grants](https://nexcmp.com/docs/images/catalog/bandwidth-03-addons.webp) ### What customers see The customer portal gets a **Bandwidth** menu with usage, remaining quota and a **buy +1 TB** action paid from the wallet (up to your per-purchase maximum). Add-ons are valid for the current period. ## Verify 1. The collector on **Summary** is running and its cursor moves forward every few minutes. 2. A VM with traffic shows usage after about 10 to 15 minutes. 3. To see the alerts, lower the VM quota to a few GB on a **test** installation and generate some traffic. Do not do this on production: the quota applies to every VM. ## Limits and notes - Usage counts per period (a month). Add-ons and quotas apply to the current period. - "Enforce actual suspend from" protects you from suspending customers the moment the module is first enabled. - The throttle policy only notifies in this release. - Changing the quota applies to every site, including reseller sites. Resellers' retail and wholesale add-on prices follow their discount or markup. - Bandwidth for **Dedicated servers** is a separate matter; see [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Usage stays at zero for a running VM | The worker is stopped, CloudStack is disconnected, or VM statistics are disabled | Check `sudo nexcmp status` and **Test connection** in [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/); enable CloudStack VM statistics. | | Usage has holes | CloudStack retention was exceeded, or the worker was down for more than about 12 hours | Not recoverable; the gap is recorded. | | Usage is a little higher than expected | Traffic between VMs and other interface traffic is included | Expected; explain this to customers. | | A suspended service comes back suspended after you unlock it | It is still over quota | Grant an add-on or raise the quota. | | Nothing is suspended although the policy is **suspend** | The suspend-from date is in the future | Set it to now (the page shows "ACTIVE" when in effect). | | "bw_over_policy must be notify, throttle or suspend" | Invalid policy sent through the API | Use one of the three values. | | Proxmox VMs show no usage | They are not metered | Expected. | | Customers cannot buy add-ons | Bandwidth is switched off, or the wallet is empty | Check the switch and the wallet. | --- # Currency and exchange rates > **Where in the admin:** Finance › Payment channels › Exchange rates (`/payments/fx`) for the rates. System › Settings › Brand (`/settings/branding`) for the **Site currency** switch. ## What it does NexCMP keeps wallets and invoices in USD. VM plan prices are stored as micro-dollars per hour. Display currencies let customers read USD amounts in their own currency at a published rate; they do not change the wallet balance. Some services accept prices entered in VND, including bandwidth add-ons, S3, Private Cloud and existing Bare Metal plans that retain VND pricing. Bank transfers through Sepay are paid in VND. Rates are **published by an administrator**. Nothing refreshes by itself, so the rates you saved stay exactly as they are until you press the update button. ## Requirements | Item | Requirement | |---|---| | Internet access | The panel server must reach `open.er-api.com` (for USD/VND) and `api.frankfurter.dev` (for the other currencies) when you press **Update exchange rates**. | | Permissions | An administrator for changes; every change asks for a reason. | | Depends on | Nothing. [Plans](https://nexcmp.com/docs/catalog/plans/), [Bandwidth](https://nexcmp.com/docs/catalog/bandwidth/), [S3](https://nexcmp.com/docs/infrastructure/s3/) and VND bank transfers use the rate. | ## Prepare the infrastructure Not required. If your server filters outbound traffic, allow HTTPS to the two services above. ## Connect in NexCMP ### 1. Choose the site currency Open **System › Settings › Brand** and set **Site currency**: | Choice | What customers see | |---|---| | **Language-based (auto)** (the default in a new installation) | The portal picks a display currency from the language (table below). The customer can choose USD instead. | | **USD** | Prices and balances in USD only. No currency switch and no converted amounts. | | **VND** | Legacy setting; follows the same language-based display rules as Auto. | Save. Customer pages update within about a minute. When the site follows the customer's language: | Language | Display currency | |---|---| | English | USD | | Vietnamese | VND | | Chinese (simplified / traditional) | CNY / TWD | | Japanese, Korean | JPY, KRW | | French, German, Spanish, Italian | EUR | | Portuguese, Russian | BRL, RUB | | Arabic, Hindi | SAR, INR | | Indonesian, Thai | IDR, THB | A currency whose rate has not been published is not offered: the portal falls back to USD. ### 2. Publish the rates 1. Open **Finance › Payment channels › Exchange rates**. 2. The card **Currency by language** lists the 11 display currencies with their rate per USD, the source (Frankfurter) and the time of the last update. Click **Update exchange rates**. The button does two things in one step: it reads the public market table for **USD/VND** and it reads the other 11 rates. Both are checked before either is saved. If one fails, nothing is changed and the previous rates remain. 3. The card **Active exchange rate** shows the USD/VND rate in effect, its source tag (public market or fixed), the margin and the market reference figures with their timestamps. 4. The card **Config** (save with a reason): | Field | Meaning | Range | |---|---|---| | Margin (bp, 100 = 1 %) | Added on top of the market reference rate. The effective rate is the reference rate plus the margin, rounded up. | 0 to 1000 | | Fixed rate (VND per USD, 0 = follow market) | A rate you set yourself. It replaces the market figure. | 0 or 15,000 to 50,000 | | Jump threshold (bp) | A new market rate that differs from the current one by more than this is **held** until you confirm it. | 50 to 2000 (default 500) | | VND rounding step | Rounds VND prices and bank-transfer amounts. | 1, 100, 1,000 (default) or 10,000 | 5. If a new market rate jumps by more than the threshold, the card **Pending exchange rate** appears and the rate is **not applied**. Check it against your chosen reference and click **Apply this rate**. A market rate outside 15,000 to 50,000 VND per USD is rejected as unreasonable. Manual refreshes are limited to one request per five minutes, so the button can ask you to wait. 6. The **Exchange rate history** table lists every fetched or confirmed rate with its time, effective rate, market reference, source, whether it was applied, who did it and a note (held jumps are marked **held**). ## How a rate is used | Place | Rule | |---|---| | Wallet and invoices | USD cents. The rate never changes a balance. | | VM plans entered in VND | Converted **once** at the rate in effect when you save, then stored in USD. Later rate changes do not reprice those VM plans. | | Bank transfer through Sepay | Customer pays `ceil(USD × rate)`, rounded up to the rounding step. Sepay is **blocked** until a USD/VND rate exists. | | Bare Metal plans | New installations use USD directly. Existing VND plans retain their price unit and need a rate when charging; see [Dedicated servers](https://nexcmp.com/docs/infrastructure/dedicated/). | | Other international payments entered in VND | Rounded to the nearest cent. | | Display in the portal | USD multiplied by the published rate of the display currency, formatted for the language. | ## Verify 1. **Active exchange rate** shows a USD/VND value with a recent "fetched" time. 2. The grid **Currency by language** shows non-empty rates. 3. In the customer portal, switch language to Vietnamese (site currency Auto): prices show in dong; switch to Japanese: yen. With site currency USD, only dollars are shown. 4. On a plan form (Plans & pricing) the **Enter price by** list offers **VND / month**. ## Limits and notes - No automatic refresh. Press **Update exchange rates** when you want new rates, and set a reminder if rates matter to you. - The public market rate is used for USD/VND; it is a reference rate rather than a bank buy or sell quote. The other display currencies come from Frankfurter; a date older than 14 days or an incomplete table is rejected. - Margin and a fixed rate affect only USD/VND. The 11 display currencies carry no margin. - Each customer language has one currency. Changing language resets to that language's currency unless the customer picked USD for the same language. - Reseller sites follow the same rates. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "New exchange rate deviates beyond threshold - ON HOLD, requires manual confirmation" | The jump is larger than the threshold | Verify and click **Apply this rate**, or change the threshold. | | "Server rejected new exchange rate" | The market figure is outside 15,000 to 50,000 or malformed | Wait and try again, or set a fixed rate. | | Refresh asks you to wait before trying again | Too frequent updates | Wait and retry. | | "could not fetch the rate: …" | The panel server cannot reach the rate services | Allow outbound HTTPS and retry. | | **Sepay blocked** tag | No USD/VND rate has been published yet | Press **Update exchange rates** or set a fixed rate. | | A currency is missing in the portal | Its rate is missing | Press **Update exchange rates**. | | Customers see prices only in USD | Site currency is USD, or the rate is missing | Set **Site currency** to Auto and publish rates. | | "Fx incomplete rate table; previous rates retained" | Frankfurter returned fewer than 11 currencies | Retry later; nothing was changed. | | The VND option is missing in the plan form | No USD/VND rate | Publish a rate first. | --- # Payment channels > **Where in the admin:** Finance › Payment channels (`/payments`). Switches and fees: Finance › Payment channels › Fees & limits (`/payments/fees`). NexCMP uses a **prepaid USD wallet**. Customers add money to the wallet through a payment channel; every service is then paid from the wallet. A channel is only shown to customers when it is both **enabled** and **configured**. ## What it does Payment channels connect your payment accounts (Stripe, PayPal, Binance, a Vietnamese bank) to the customer wallet. A confirmed payment becomes exactly one wallet credit — each credit is keyed by the gateway payment ID, so a repeated webhook can never add money twice. Use this page to see which channels are ready, how they performed over the last 30 days, and which ones need attention. ## Requirements | Item | Requirement | |---|---| | Public HTTPS address | Gateways send webhooks to `https:///v1/billing/...`, so your panel must be reachable from the Internet over valid TLS (see [DNS and Cloudflare](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/)). | | Accounts | Your own account with each provider you want to use. Credentials are entered in the admin and are never shown again (only the last 4 characters). | | Admin role | An administrator account. | | Dependencies | None. The VN bank channel additionally needs an [exchange rate](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). | ## The channels | Channel | What the customer sees | Set up in | Article | |---|---|---|---| | Card (Stripe) | Card form, saved card | Payment channels › Stripe | [Stripe](https://nexcmp.com/docs/billing/stripe/) | | Apple Pay, Google Pay, Link | Wallet buttons (Stripe Express Checkout) | Stripe, plus domain registration | [Stripe](https://nexcmp.com/docs/billing/stripe/#apple-pay-google-pay-and-link) | | Alipay | Redirect to Alipay (charged in JPY by Stripe) | Stripe, plus Fees & limits | [Stripe](https://nexcmp.com/docs/billing/stripe/#alipay) | | PayPal | PayPal buttons, saved PayPal account | Payment channels › PayPal | [PayPal](https://nexcmp.com/docs/billing/paypal/) | | USDT | Deposit address and exact amount | Payment channels › USDT | [USDT](https://nexcmp.com/docs/billing/usdt/) | | VN bank transfer | QR code and transfer details (Vietnamese interface only) | Payment channels › VN bank transfer | [Bank transfer (Vietnam)](https://nexcmp.com/docs/billing/bank-transfer-vn/) | ## Prepare the infrastructure Not required. Everything is configured in the admin; there is nothing to install on the server. ## Connect in NexCMP 1. Open **Finance › Payment channels** (`/payments`). The overview table lists every channel with its state. 2. Open the channel's own tab (**Stripe**, **PayPal**, **USDT**, **VN bank transfer**) and follow its article. Each tab ends with a **Save** button and, where available, a **Test** button. 3. Open **Fees & limits** (`/payments/fees`), find the card **Payment gateways & fees (wallet top-up)**, turn the channel on with its switch and click **Save channels & fees**. ![Payment channels overview listing the gateways with their enabled, configured and 30-day statistics](https://nexcmp.com/docs/images/billing/payment-channels-01-overview.webp) ![Fees and limits page with the channel switches and the fee fields](https://nexcmp.com/docs/images/billing/payment-channels-02-fees.webp) ### Switches and fees (Fees & limits) | Setting | Default | Meaning | |---|---|---| | Card (Stripe) | On | Card form on the deposit screen. Hidden until Stripe has a secret key. | | Apple Pay, Google Pay, Link | On | Wallet buttons. They only appear when the domain is verified at Stripe. | | Alipay | Off | Needs Alipay activated in the Stripe Dashboard. | | PayPal | Off | Shown only when PayPal is configured. | | USDT (Binance) | Off | The switch is locked until the deposit address has been verified against Binance. | | Stripe fee % / fixed (cents) | 3.6 % and 0 | Used to show the customer the fee and to compute the credited amount. Set it to your real Stripe tariff. | | PayPal fee % / fixed | 4.4 % and 30 cents | Same, for PayPal. | | USDT fee % | 1 % | Same, for USDT. | | Customer pays top-up fee | Off | **On**: the customer pays amount + fee and the wallet receives the full amount. **Off**: the fee is deducted and the wallet receives the amount minus the fee. | The customer always sees the fee and the final wallet credit before paying. Turning a channel off hides the button and the server refuses new payments with `method_disabled` (HTTP 403). ::: tip Top-up limits The minimum and maximum amount per top-up are on the same page — see [Minimum top-up](https://nexcmp.com/docs/billing/minimum-top-up/). ::: ## Verify 1. On the overview table the channel shows **Enabled** and **Configured**; the **Last test** column is filled after you click **Test** on the channel's tab. 2. Sign in to the portal as a test customer and open **Billing › Deposit**. The channel button is present. 3. Make a small payment (see the channel's article for test cards or sandbox accounts). The balance rises, a **Top-up received** notification appears, and the payment is listed in **Finance › Billing › Top-ups** (`/billing/deposits`). ## Limits & notes - **Overview columns:** *30 days* (number of payments, successes, failures and volume), *Stuck 24h* (payments still pending after a day) and *Unmatched* (USDT or bank transfers that arrived but match no order). The KPI row adds the 30-day volume, the number of faulty channels (enabled but not configured, or stuck payments) and the total unmatched. - **Webhook events** are listed under **Payment channels › Gateway events** (`/payments/events`). Stripe has its own tab; PayPal and USDT share one. A failed event can be replayed — the wallet ledger is idempotent, so replaying never credits twice. - **Resellers** can have their own Stripe, PayPal, USDT and bank channels. Their rows also appear here, and the site selector in the header filters them. See [Resellers](https://nexcmp.com/docs/customers/resellers/). - **Refunds:** Stripe and PayPal payments can be refunded from the payment list; USDT payments cannot be refunded to the source (the server answers `usdt_no_refund`). - **Maintenance mode** still accepts gateway webhooks, so a payment made just before maintenance is not lost. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Channel missing on the customer's deposit screen | Switched off, not configured, or (VN bank) the interface is not Vietnamese | Check **Fees & limits** and the channel tab; see [Bank transfer (Vietnam)](https://nexcmp.com/docs/billing/bank-transfer-vn/). | | The KPI **Faulty channels** is above 0 | A channel is enabled without credentials, or payments are pending for more than 24 hours | Open the row and click **Configure**; for stuck payments open **Gateway events** and replay. | | Customer paid but the balance did not change | The webhook did not arrive | Check the webhook URL on the channel tab, then replay the event in **Gateway events**. | | Server answers `method_disabled` | The switch is off | Turn it on in **Fees & limits** and save. | | USDT switch is greyed out | Deposit address not verified | Click **Test & verify address** on the USDT tab. | --- # Stripe > **Where in the admin:** Finance › Payment channels › Stripe (`/payments/stripe`). Switches and fees: Finance › Payment channels › Fees & limits (`/payments/fees`). ## What it does Stripe takes card payments for wallet top-ups. With the same account you can also offer **Apple Pay**, **Google Pay**, **Link** and **Alipay**, and let customers **save a card** for [automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/). Use it as the primary channel for international customers. ## Requirements | Item | Requirement | |---|---| | Stripe account | A Stripe account that can accept payments in **USD**. Alipay is charged in JPY and has to be activated by Stripe for your account. | | Keys | A secret key (`sk_live_...` or `sk_test_...`) and a publishable key (`pk_live_...` or `pk_test_...`). The form only accepts keys with these prefixes. | | Network | Your panel must be reachable over HTTPS from the Internet (Stripe calls the webhook) and must reach `api.stripe.com` on port 443. | | Domain (wallets only) | The customer-facing domain has to serve `/.well-known/apple-developer-merchantid-domain-association` — NexCMP does this for you. | | Dependencies | None. | ::: info Shared Stripe accounts NexCMP only processes webhook events for payments it created itself, so you can use a Stripe account that also serves another application. ::: ## Prepare the infrastructure On the Stripe side: 1. Sign in to the [Stripe Dashboard](https://dashboard.stripe.com). Switch **Test mode** on or off to decide whether you are doing a trial run or going live. Keys of one mode never work with the other. 2. Open **Developers › API keys**. Copy the **Publishable key** and reveal and copy the **Secret key**. Keep the secret key private. 3. (Optional) Open **Settings › Payment methods** and activate **Alipay** if you want to offer it. 4. You do **not** have to create the webhook by hand — NexCMP can create it for you (see step 5 below). If you prefer to do it manually, create an endpoint in **Developers › Webhooks** with the URL and events listed in [Webhook](#webhook). ## Connect in NexCMP 1. Open **Finance › Payment channels › Stripe** (`/payments/stripe`). 2. In the **API keys** card choose **Mode** (`live` or `test`), paste the **Secret key** and the **Publishable key**. 3. Click **Save**. The keys are written to a configuration file on the server that only the NexCMP service account can read. They are never sent back to the browser; afterwards the page shows only the last 4 characters. 4. Click **Check**. A dialog reports the connection, mode, available and pending balance, account ID, whether charges are enabled, whether a webhook exists and whether Alipay is available. A red line means that part needs attention. 5. In the **Webhook** card click **Create webhook** and confirm. NexCMP asks Stripe to create the endpoint with every required event and fills **Webhook signing secret** (`whsec_...`) automatically. The URL pre-filled in the field is `https:///v1/billing/stripe/webhook`, for example `https://cloud.example.com/v1/billing/stripe/webhook`. 6. Open **Fees & limits** (`/payments/fees`), keep **Card (Stripe)** on, set **Stripe fee %** and **Stripe fixed fee (cents)** to your real tariff and click **Save channels & fees**. See [Payment channels](https://nexcmp.com/docs/billing/payment-channels/#switches-and-fees-fees-limits). ![Stripe page with the mode, secret key and publishable key filled in with example values](https://nexcmp.com/docs/images/billing/stripe-01-keys.webp) ### Webhook The red banner "Missing webhook_secret" stays until a signing secret is saved. Without it Stripe top-ups are **not recorded**, even though Stripe took the money. | Item | Value | |---|---| | URL | `https:///v1/billing/stripe/webhook` | | Events | `checkout.session.completed`, `payment_intent.succeeded`, `payment_intent.payment_failed`, `charge.refunded`, `charge.dispute.created`, `charge.dispute.closed`, `radar.early_fraud_warning.created`, `refund.updated`, `refund.failed` | | Signature | Stripe `Stripe-Signature` header, HMAC-SHA256, 300 seconds tolerance. Requests with a bad signature get HTTP 400. | | Idempotency | Each event ID is stored once; a repeated delivery never credits twice. | If you upgraded from a release that registered fewer events, click **Create webhook** again — the old endpoint is kept, so delete it by hand in the Stripe Dashboard afterwards. The **24h events** and **Latest event** rows in the Webhook card tell you whether Stripe is reaching the panel. Individual events are listed in **Payment channels › Gateway events** (`/payments/events`, tab Stripe), where each event can be viewed (**Payload**) or replayed (**Replay**). ### Apple Pay, Google Pay and Link 1. In the **Wallets (Apple Pay · Google Pay · Link) & Alipay** card, the field **Customer app Domain registered with Stripe** is pre-filled with your site's host name (for example `cloud.example.com`). 2. Click **Register / verify domain** and confirm. NexCMP registers the domain in Stripe's payment method domains and asks Stripe to validate it. 3. The result shows the status of **Apple Pay**, **Google Pay** and **Link**. Apple Pay shows only when the status is `active`. 4. In **Fees & limits** keep **Apple Pay · Google Pay · Link** on. ![Wallet card showing the registered domain and the Apple Pay, Google Pay and Link statuses](https://nexcmp.com/docs/images/billing/stripe-03-wallets.webp) ::: tip Apple Pay stays inactive Apple Pay needs the domain-association file to answer HTTP 200 on your public domain. If you put Cloudflare or another proxy in front, do not block `/.well-known/`. ::: ### Alipay Alipay is optional. Activate it in **Stripe Dashboard › Settings › Payment methods**, click **Check** and wait until the dialog shows that Alipay is available, then turn on **Alipay (Stripe, JPY display)** in **Fees & limits**. Set **Alipay fee %**, **Alipay fixed fee**, the **¥/USD rate** (50–500) and the **FX spread %** (0–20). Update the ¥/USD rate regularly; the page warns when it is older than 7 days. ## Verify 1. In test mode, sign in to the portal as a test customer, open **Billing**, click **Deposit**, choose **Card** and pay the minimum with the Stripe test card `4242 4242 4242 4242` (any future date, any CVC). 2. The balance rises within seconds and the payment appears under **Finance › Billing › Top-ups** with the status *Completed*. 3. Open **Payment channels › Gateway events › Stripe**: you should see `checkout.session.completed` or `payment_intent.succeeded` with a processed time. 4. Back on the Stripe page, **24h events** is above 0. Before going live, switch **Mode** to `live`, paste the live keys and click **Create webhook** again (webhooks of test and live mode are separate). ## Limits & notes - **Amounts and currency.** Wallets are in USD. Card, wallet and link payments are charged in USD; Alipay is shown to the customer in JPY and converted at your rate. - **Fees.** Stripe's real fee is not read back automatically; the fee percentage you set decides what the customer sees and what the wallet is credited. Choose **Customer pays top-up fee** in *Fees & limits* to charge the fee on top. - **Saved cards.** When the customer ticks *Save card for auto top-up* the card is stored at Stripe, not on your server. See [Saved payment methods](https://nexcmp.com/docs/billing/saved-payment-methods/). - **Refunds.** Refunding to the card is a two-step process: an admin creates a refund request for a completed payment (**Finance › Billing › Top-ups**, refund action on the payment) and then approves it under **Finance › Payouts › Card refunds** (`/billing/payouts`). Alipay refunds are asynchronous; a failed one (`refund.failed`) returns the held amount to the wallet. - **Disputes.** On `charge.dispute.created` NexCMP blocks the customer's account, stops their prepaid services (nothing is deleted) and sends an admin alert. If you win (`charge.dispute.closed`), the amount is credited back to the wallet. - **Waiting payments.** If a webhook never arrives, NexCMP checks payments that stayed pending for more than 10 minutes against Stripe on its own and completes them. Payments older than 7 days are ignored. - **Resellers** can connect their own Stripe account (webhook `https:///v1/billing/stripe/webhook/r/`); see [Resellers](https://nexcmp.com/docs/customers/resellers/). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Secret key must start with sk_live_/sk_test_" | Wrong key pasted | Paste the secret key from **Developers › API keys**. | | "Secret key does not match live/test mode" | Mode and key disagree | Make the **Mode** match the prefix of the key. | | "Publishable key must start with pk_" | Wrong key | Use the publishable key. | | Check says the connection failed | Wrong key, key revoked, or the server cannot reach `api.stripe.com` | Re-paste the key; test with `curl -I https://api.stripe.com` from the server. | | Red banner "Missing webhook_secret" | The signing secret is not saved | Click **Create webhook** or paste the `whsec_...` secret from the Stripe Dashboard and **Save**. | | Money taken but the balance did not change | Webhook not delivered or signature failing | Check the URL ends with `/v1/billing/stripe/webhook`, the secret matches the endpoint, and replay the event in **Gateway events**. In Stripe, open the endpoint and look at the delivery attempts (HTTP 400 means a wrong signing secret). | | "Create webhook" fails | The URL is not HTTPS or not reachable | Use your public `https://` domain. | | Apple Pay status not active | Domain not validated | Click **Register / verify domain** again after the domain-association file answers 200. | | Alipay not available in the Check dialog | Not activated at Stripe | Activate it in the Stripe Dashboard, then click **Check**. | | Customer sees no card form | **Card (Stripe)** is off or no secret key | Turn it on in **Fees & limits** and save keys. | --- # PayPal > **Where in the admin:** Finance › Payment channels › PayPal (`/payments/paypal`). The on/off switch and fees are in Finance › Payment channels › Fees & limits (`/payments/fees`). ## What it does Lets customers top up their USD wallet with their PayPal balance, bank account or card held at PayPal. Customers can also let NexCMP remember their PayPal account for [automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/). Use it alongside [Stripe](https://nexcmp.com/docs/billing/stripe/) for customers who prefer PayPal. ## Requirements | Item | Requirement | |---|---| | PayPal account | A PayPal **Business** account that can receive USD. | | REST app | One REST app in the PayPal Developer Dashboard (**Apps & Credentials**) providing a **Client ID** and a **Client secret**. Use the **Sandbox** tab for testing, **Live** for production. | | Network | HTTPS reachable from the Internet (PayPal calls the webhook) and outbound access to `api-m.paypal.com` (live) or `api-m.sandbox.paypal.com` (sandbox), port 443. | | Permissions | The REST app needs the refund and webhooks capabilities. The **Check** dialog shows whether both scopes are present. | | Dependencies | None. | ## Prepare the infrastructure 1. Sign in to [developer.paypal.com](https://developer.paypal.com) with your Business account and open **Apps & Credentials**. 2. Choose **Sandbox** (testing) or **Live**, then **Create App**. Name it for example "NexCMP". 3. Copy the **Client ID** and **Secret** of the app. 4. For testing, use **Testing Tools › Sandbox Accounts** to find or create a *Personal* (buyer) sandbox account. 5. You do not need to create the webhook yourself; NexCMP creates it with the **Create webhook** button. ## Connect in NexCMP 1. Open **Finance › Payment channels › PayPal** (`/payments/paypal`). 2. In **API keys**, choose **Mode**: `live` (real money, `api-m.paypal.com`) or `sandbox` (`api-m.sandbox.paypal.com`). 3. Paste the **Client ID** (at least 20 characters) and the **Client secret**. 4. Click **Check**. It tests the credentials you typed (saving is not required) and reports the connection, App ID, mode, whether the refund and webhooks scopes are present and how long the token lives. 5. Click **Save**. 6. In the **Webhook** card confirm the URL is `https:///v1/billing/paypal/webhook` (it must be `https`), then click **Create webhook** and confirm. NexCMP asks PayPal to create or update the webhook with 11 events and stores the returned **Webhook ID**. The button uses the **saved** keys — save first if you just edited them. 7. Click **Check** again: the **Webhook** row should say "exists, 11 events". 8. Open **Fees & limits**, turn **PayPal** on, set **PayPal fee %** (default 4.4) and **PayPal fixed fee (cents)** (default 30) to your tariff and click **Save channels & fees**. ![PayPal page with sandbox mode and example credentials entered](https://nexcmp.com/docs/images/billing/paypal-01-keys.webp) ### Webhook events NexCMP registers these 11 events: `CHECKOUT.ORDER.APPROVED`, `PAYMENT.CAPTURE.COMPLETED`, `PAYMENT.CAPTURE.PENDING`, `PAYMENT.CAPTURE.DECLINED`, `PAYMENT.CAPTURE.REFUNDED`, `PAYMENT.CAPTURE.REVERSED`, `CUSTOMER.DISPUTE.CREATED`, `CUSTOMER.DISPUTE.UPDATED`, `CUSTOMER.DISPUTE.RESOLVED`, `RISK.DISPUTE.CREATED`, `CHECKOUT.PAYMENT-APPROVAL.REVERSED`. Each webhook is verified by calling PayPal's signature-verification API, stored once and processed idempotently. NexCMP never trusts the amount in the webhook body: before it credits a capture it fetches the capture from PayPal. ## Verify 1. Keep **Mode** on `sandbox`. In the portal, open **Billing › Deposit**, choose **PayPal**, click **Continue on PayPal** (or the PayPal button) and log in with the sandbox buyer account. 2. Approve the payment. You return to the portal, the balance rises and the payment appears under **Finance › Billing › Top-ups** as *Completed*. 3. Open **Payment channels › Gateway events › PayPal & USDT**: the `PAYMENT.CAPTURE.COMPLETED` event is listed. 4. When everything works, switch **Mode** to `live`, enter the live credentials, **Save**, then **Create webhook** again (sandbox and live webhooks are separate). ## Limits & notes - **How a payment completes.** The order is created in USD; after the customer approves it, the **server** captures it (idempotent request ID) and credits the wallet exactly once. The webhook and a reconciliation job that runs every 10 minutes are safety nets if the customer closes the browser. - **Pending and abandoned orders.** An order can stay open for 12 hours. A customer can have at most 5 pending orders (the server answers `too_many_pending`, HTTP 429). An order still not captured after 24 hours is marked failed. - **No webhook configured.** The panel answers HTTP 503 to every PayPal webhook until a Webhook ID is saved. Captures made through the portal and the 10-minute reconciliation still credit the wallet, but nothing that PayPal reports by webhook (disputes, reversals, late captures) is applied — click **Create webhook**. - **Saved PayPal account.** When the customer chooses to save PayPal (or completes the first deposit, which turns on [automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/)) PayPal stores the payment token in its vault. NexCMP keeps only an identifier and the PayPal e-mail. See [Saved payment methods](https://nexcmp.com/docs/billing/saved-payment-methods/). - **Refunds and disputes** are handled like Stripe: refund requests are approved under **Finance › Payouts › Card refunds**; a dispute blocks the customer's account and stops their prepaid services until you resolve it. - **Fees.** PayPal's fee is not read back; the percentage and fixed fee you configure determine what the customer sees and what is credited. The real tariff depends on your country (the form hints 4.4 % for Vietnam and 3.49 % for the US). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Client ID too short" | Less than 20 characters pasted | Copy the full Client ID. | | "Webhook URL must be https" | URL typed with `http://` | Use your public `https://` domain. | | Check reports a connection error | Wrong mode (live keys in sandbox or the reverse), wrong secret, no outbound access | Match **Mode** to the key type; test `curl -I https://api-m.paypal.com` from the server. | | Scope refund / webhooks shows "no" | The REST app lacks the features | Enable the features on the app in the PayPal Developer Dashboard. | | Banner "PayPal key set, NO webhook" | Webhook ID missing | Click **Create webhook**. | | "Webhook not found" in Check | Webhook was deleted at PayPal or belongs to another app | Click **Create webhook** again. | | Customer is told PayPal declined the payment | PayPal rejected that funding source | Ask the customer to choose another payment source. | | Customer sees "Too many pending requests" | 5 open orders | Wait for them to expire or finish them. | | PayPal button missing | PayPal off in **Fees & limits** or not configured | Turn it on and save credentials. | --- # USDT (Binance) > **Where in the admin:** Finance › Payment channels › USDT (`/payments/usdt`). Switch and fee: Finance › Payment channels › Fees & limits (`/payments/fees`). Unmatched deposits: Finance › Payment channels › Gateway events › PayPal & USDT (`/payments/events?tab=gateways`). ## What it does Customers send USDT to **your Binance deposit address**; NexCMP reads the deposit history through a **read-only** Binance API key and credits the wallet when a deposit matches an open request. There is no payment-processor fee on your side other than what Binance charges, and no private keys are ever held by NexCMP. ## Requirements | Item | Requirement | |---|---| | Binance account | A verified Binance account with USDT deposits enabled on the network you choose. | | API key | A Binance API key with **Enable Reading** only. Enable Withdrawals, Internal Transfer and Spot/Margin Trading must be **off**. | | IP restriction | Strongly recommended: restrict the key to the server's outbound IP (the **Test & verify address** dialog shows it). | | Network | Outbound HTTPS to `api.binance.com`. No inbound port is needed. | | Supported networks | `TRX` (Tron, TRC20), `BSC` (BNB Smart Chain, BEP20), `SOL` (Solana), `ETH` (Ethereum, ERC20). Coin is fixed to USDT. | | Dependencies | None. | ::: warning Choose the network once Customers who send on the wrong network lose the funds. Pick the network with low fees that your customers' exchanges support (TRX and BSC are the usual choices), and do not change it often. ::: ## Prepare the infrastructure 1. In Binance open **Account › API Management** and create a new API key (system generated). Give it a label such as "NexCMP". 2. Edit the permissions: leave only **Enable Reading** checked. Do not enable withdrawals, transfers or trading. 3. Under **Restrict access to trusted IPs only** add the public IP address of your NexCMP server. If you do not know it yet, create the key without the restriction, run the first test in NexCMP (it prints the outbound IP) and then add the IP. 4. Copy the **API key** and **Secret key** (the secret is shown only once). 5. Open **Wallet › Deposit › Crypto**, choose **USDT** and the **network** you will use, and copy the **deposit address** (for example a `T...` address on TRX or `0x...` on BSC/ETH). ## Connect in NexCMP 1. Open **Finance › Payment channels › USDT** (`/payments/usdt`). 2. In **API key (Binance › API Management)** paste **API key** and **API secret**. Both must be entered together. 3. In **Deposit address** choose the **Network** and paste the **USDT deposit address on the selected network**. The format is checked against the network (`T` + 33 characters for TRX, `0x` + 40 hex characters for BSC and ETH, base58 for SOL). 4. Click **Save**. Changing the address or network resets the verification and pauses the channel until you verify it again; the page asks you to confirm. 5. Click **Test & verify address**. (Unsaved changes are refused — the check runs on the saved configuration.) The dialog shows: - key permissions (reading, withdrawals, internal transfer, trading, IP restriction) — red warnings for dangerous permissions; - the server's outbound IP (to enter in Binance); - the network (deposits open or closed, required confirmations, dust); - the address Binance issues for that network and whether it **matches** yours. 6. When the address matches, NexCMP records the verification time. Only then can you open **Fees & limits**, turn on **USDT (Binance)**, check **USDT fee %** (default 1) and **USDT request expires after (mins)** (5–180, default 30), and click **Save channels & fees**. ## How a payment is matched 1. The customer enters an amount in **Billing › Deposit › USDT** and clicks **Get deposit address**. 2. NexCMP creates a request and shows the address, the network and an **exact amount** with six decimals — for example `19.173742`. The last four decimals are a random nonce that makes the amount unique to that request. 3. The customer must send **exactly** that amount on that network. A payment that does not match (rounded amount, wrong network) cannot be assigned automatically. 4. Every 60 seconds NexCMP reads the last 24 hours of Binance deposits. A matching deposit moves the request to *confirming* and, once Binance has the required confirmations, credits the wallet. A request that has seen funds never expires while confirmations are pending. 5. A request not paid within the expiry time (default 30 minutes) is marked expired. ## Verify 1. After the address is verified and USDT is switched on, sign in as a test customer and request a small deposit (the minimum top-up). 2. Send the exact amount from another wallet or exchange. Allow a few minutes. 3. The portal shows *Transfer seen — confirming*, then *USDT received — balance updated*. 4. The USDT page shows a recent **Last poll** time and **Unmatched** at 0. ## Limits & notes - **Rate.** 1 USDT = 1.00 USD. The fee in **USDT fee %** is shown to the customer and either deducted from or added to the amount depending on **Customer pays top-up fee**. - **No refunds.** USDT payments are never refunded to the source by NexCMP (HTTP 409 `usdt_no_refund`). Refund manually outside the system if you must. - **Pending limit.** A customer has a limited number of open requests; beyond that the server answers `too_many_pending` (HTTP 429). - **Resellers** can use their own Binance account; see [Resellers](https://nexcmp.com/docs/customers/resellers/). - **If you share the Binance account** with another system, give each system a different network so two pollers never see the same deposit. ### Unmatched deposits A deposit that matches no request (wrong amount, wrong network, expired request) creates a `deposit.unmatched` event and an admin alert. Open **Payment channels › Gateway events › PayPal & USDT**, find the event, check the transaction on Binance and click the assign action to credit the correct customer. The **Unmatched** counter on the USDT page and on the channels overview drops to 0 afterwards. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "API key and secret must be entered together" | Only one field filled | Fill both. | | "Address is not in the format of network ..." | Address does not fit the selected network | Re-copy it from Binance for the same network. | | "Unsaved changes — Save first then Test" | Test runs on saved settings | Click **Save**, then test. | | Warning "key has Enable Withdrawals" | Excess permission | Remove it in Binance API Management. | | Warning "key not restricted to IP" | `ipRestrict` is off | Add the server's outbound IP shown in the dialog. | | Warning "key lacks Enable Reading" | Permission missing | Enable Reading. | | Warning "Binance has deposits disabled for USDT on the network" | Network suspended | Choose another network or wait. | | "DANGER: configured address differs from the address Binance issues" | Wrong address or network | Copy the address again from Binance for that network. | | USDT switch greyed out in Fees & limits | Address not verified | Run **Test & verify address** until it matches. | | Customer paid, nothing credited | Rounded amount or wrong network | Find the event in Gateway events and assign it by hand. | | Deposit seen but waiting a long time | Not enough confirmations yet | Wait; the request does not expire while confirming. | --- # Bank transfer (Vietnam) > **Where in the admin:** Finance › Payment channels › VN bank transfer (`/payments/bank`). Exchange rate: Finance › Payment channels › Exchange rates (`/payments/fx`). ## What it does Lets customers top up the USD wallet by a normal bank transfer in Vietnamese dong. NexCMP shows a VietQR code, the account details and a unique **transfer memo**; [Sepay](https://sepay.vn) watches your bank account and calls a webhook when the money arrives, and NexCMP credits the wallet. Use it if you sell to customers in Vietnam. ::: info Shown only in the Vietnamese interface The VN bank option is offered to a customer only when the portal is displayed in **Vietnamese**. Customers on any other language never see it. ::: ## Requirements | Item | Requirement | |---|---| | Bank account | A Vietnamese bank account (or a Sepay virtual account, **VA**) connected to a Sepay account at [my.sepay.vn](https://my.sepay.vn). | | Sepay | A Sepay account with webhook integration enabled. | | Exchange rate | A USD to VND rate must exist: update it under **Exchange rates** first. Without a rate the channel answers `fx_unavailable` and customers cannot start a transfer. | | Network | Your panel must be reachable by HTTPS so Sepay can call `https:///v1/billing/sepay/webhook`. | | Dependencies | [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). | ## Prepare the infrastructure On the Sepay side: 1. In [my.sepay.vn](https://my.sepay.vn) connect your bank account (or create a VA) and make sure transactions appear. 2. Decide the **memo prefix** — 2 to 5 capital letters, for example `PAY`. NexCMP generates the memo as the prefix followed by 8 random digits (for example `PAY48210937`). The prefix must not contain `ORD`, and it should not be used by any other system that receives money into the same account. 3. If your Sepay account uses a *payment code structure*, register the same prefix there so Sepay recognises the memo. 4. Create the webhook later, after the NexCMP page has given you the URL and key (step 7 below). ## Connect in NexCMP 1. Open **Finance › Payment channels › Exchange rates** and click **Update exchange rates** if no rate has been stored. 2. Open **Finance › Payment channels › VN bank transfer** (`/payments/bank`). 3. In **Receiving account** fill in: | Field | Example | Rule | |---|---|---| | Bank | `VCB` Vietcombank | Choose from the list; the BIN fills in automatically. | | Account number | `0123456789` | 4–24 digits or capital letters. | | Virtual account (VA — optional) | | If set, customers transfer to the VA and the QR shows it. | | Account holder name | `NGUYEN VAN A` | No accents, UPPERCASE, 2–60 characters. | | Transfer memo prefix | `PAY` | 2–5 capital letters, no `ORD`. | 4. In **Webhook authentication** choose **Auth method**: **HMAC-SHA256** (recommended) or **API key**, and enter the **Webhook key / HMAC secret** (8–200 characters). Optionally enter the **userapi token** from Sepay; it is used only to reconcile transfers whose webhook was lost. 5. In **Channel & limits** set the limits (defaults in brackets): **Min / order** (10,000 VND), **Max / order** (200,000,000 VND), **Order expiry** (120 minutes, 15–1440), **Max pending orders / account** (3, 1–10), **Reconcile every** (10 minutes) and, optionally, **Allowed webhook IPs** (comma-separated; empty means the IP is only logged). 6. Click **Save**. The red banner "Sepay not configured" disappears once the account, name, prefix and key are valid. 7. Copy **Webhook URL to paste into my.sepay.vn** (it reads `https:///v1/billing/sepay/webhook`) with **Copy**. In Sepay open the webhook integration, paste the URL, choose the same authentication type and key, and enable the "money in" event. 8. Click **Test connection**. Then switch on **Enable Sepay for clients (main site)** and **Save**. ![VN bank transfer page with the receiving account, memo prefix and webhook authentication filled in with example values](https://nexcmp.com/docs/images/billing/bank-transfer-vn-01-config.webp) ![Channel and limits card with the Sepay switch turned on and the default limits](https://nexcmp.com/docs/images/billing/bank-transfer-vn-02-limits.webp) ### Webhook security | Method | What Sepay must send | |---|---| | HMAC-SHA256 | Header `X-SePay-Signature: sha256=`, computed over `"."`; the timestamp may differ by at most 300 seconds. | | API key | Header `Authorization: Apikey `. | Only the headers are checked (never the body or query string) and the comparison is constant-time. Only incoming transfers (`transferType = in`) to your configured account or VA are used. Each Sepay transaction is stored once, so a retried webhook never credits twice. NexCMP answers `200 {"success":true}` even when it deliberately ignores a transaction, and HTTP 5xx only for temporary errors so Sepay retries. ## How the customer pays 1. In **Billing › Deposit** (Vietnamese interface) the customer chooses the VN bank transfer option and an amount, then clicks the create-transfer button. 2. NexCMP converts the USD amount at the stored rate (including the margin and rounding step you set in **Exchange rates**) and **rounds the VND amount up** to the rounding step. 3. The customer sees a QR code (VietQR, generated locally) with the account number, bank, amount and memo. 4. After the transfer, Sepay calls the webhook, NexCMP credits `floor(VND received × 100 / rate)` cents. The rate is locked when the order is created; if the money arrives after the order expired, the higher of the locked and the current rate is used. 5. Orders expire after **Order expiry**; reconciliation runs every **Reconcile every** minutes as a safety net (needs the userapi token). ## Verify 1. Create a test order as a customer on the Vietnamese interface. Check that the amount, account and memo are correct. 2. Transfer the smallest allowed amount with the exact memo. 3. Within a minute the portal shows the deposit as completed and the wallet rises; the **State** card on the bank page shows the last webhook time. 4. Open **Finance › Billing › Top-ups** and check the payment is listed with the VND amount received. ## Limits & notes - **Minimum.** The VND minimum a customer sees is the larger of **Min / order** and the VND value of the [minimum top-up](https://nexcmp.com/docs/billing/minimum-top-up/). - **Pending orders.** A customer can have only **Max pending orders / account** open orders (default 3). - **Same memo twice.** Transferring twice with the same memo creates a second completed payment at the current rate. - **No automatic refund** to a bank account. - **Fee.** The channel has no fixed fee line in **Fees & limits**; the rate margin is the only adjustment. - **Resellers** can configure their own account; their webhook URL ends with `/v1/billing/sepay/webhook/r/`. ### Unmatched transfers Money that reached the account but carries no valid memo appears in **Unmatched transactions** at the bottom of the page (filter *All channel owners* or *Master*). Check the bank reference with the customer, then click **Assign** and select the account. The wallet is credited at the current rate. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Banner "Sepay not configured" | Bank, account, holder name, prefix or key missing or invalid | Complete the **Receiving account** and **Webhook authentication** cards. | | Banner "USD/VND rate blocked" (`fx_unavailable`) | No exchange rate stored | **Exchange rates › Update exchange rates**. | | `prefix: 2–5 capital letters, no ORD` | Prefix invalid | Use 2–5 capital letters without `ORD`. | | `account_name` error | Accents or lowercase | Use unaccented UPPERCASE letters, digits, space, dot or hyphen. | | `webhook_secret: 8–200 characters` | Key too short | Use at least 8 printable characters. | | Customer cannot see the option | Not in Vietnamese interface, switch off, or no rate | Check the language, the switch and the rate. | | Customer paid but nothing credited | Webhook did not arrive, memo changed, wrong amount | Check Sepay's webhook log; the transfer will be under **Unmatched transactions** if the memo was wrong. | | Webhook returns 401 or 403 | Auth method or key mismatch | Use the same method and key in Sepay and NexCMP. | | Customer sees "Too many pending requests" | Pending order limit reached | They finish or wait for expiry. | --- # Minimum top-up > **Where in the admin:** Finance › Payment channels › Fees & limits (`/payments/fees`), card **Wallet & top-up (Billing › Policy)**. ## What it does Every payment channel rejects an amount below the **minimum top-up** or above the **maximum top-up per transaction**. The defaults are **$10.00** and **$5,000.00**. Use the minimum to avoid tiny payments that cost more in gateway fees than they are worth. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Dependencies | None. | ## Set the limits 1. Open **Finance › Payment channels › Fees & limits** (`/payments/fees`). 2. In the card **Wallet & top-up (Billing › Policy)** edit **Min top-up (USD)** and **Max top-up per transaction (USD)**. 3. Click **Save Wallet & top-up**. ![Wallet and top-up card with the minimum top-up 10.00 and the maximum 5000.00](https://nexcmp.com/docs/images/billing/minimum-top-up-01-limits.webp) | Setting | Default | Allowed range | Rule | |---|---|---|---| | Min top-up (USD) | $10.00 | $0 to $10,000 | Must be less than or equal to the maximum. | | Max top-up per transaction (USD) | $5,000.00 | $1 to $1,000,000 | Must be at least the minimum. | The values are stored in cents (`min_topup_cents`, `max_topup_cents`). ## Where the limits apply | Place | Behaviour | |---|---| | Card, Apple/Google Pay, Link, Alipay, PayPal, USDT | Amounts outside the range are refused by the server. The portal shows "Enter an amount between {min} and {max}". | | VN bank transfer | The VND amount must be inside the Sepay limits **and** its USD value (what the wallet receives) inside the minimum and maximum. The portal shows the larger of the two VND minimums. See [Bank transfer (Vietnam)](https://nexcmp.com/docs/billing/bank-transfer-vn/). | | [Automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/) | Every automatic charge is at least the minimum and at most the maximum. In fixed mode the customer's amount must be between the minimum and $1,000. | | Top-up codes ([Vouchers](https://nexcmp.com/docs/billing/promotions/)) | Not affected. | | Admin credit / debit | Not affected. | | Reseller wallet top-up | A separate minimum (`rs_topup_min_cents`, default $20) under Resellers › Defaults. | ## Related floors The minimum top-up is not the only balance rule. These are in **Services › Virtual Machines › Policy** (`/vm/policy`): | Rule | Default | Meaning | |---|---|---| | Minimum balance (`min_balance_cents`) | $5.00 | The least wallet balance a customer needs to create a VM. | | Quotas | Off | When **Quotas** are on, creating a VM also needs a balance covering a number of hours of that VM. See [Quotas](https://nexcmp.com/docs/billing/quotas/). | ## Verify 1. In the portal open **Billing › Deposit** and type an amount just under your minimum. The pay button is disabled and the range message appears. 2. Enter the minimum and confirm the payment starts. ## Limits & notes - A change applies to new payments. Orders already created keep the amounts they were created with. - The customer-facing hint ("Minimum $10.00, maximum $5,000.00") and the quick-amount buttons follow the new limits automatically. - If you display VND (the **Exchange rates** page), the minimum is converted at the stored rate for display only; the wallet and the limits stay in USD. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Minimum top-up must be less than or equal to the maximum" | Values crossed | Lower the minimum or raise the maximum. | | Customer cannot pay a small amount through VN bank transfer | Sepay VND minimum or the USD minimum is higher | Lower **Min / order** on the bank page or the USD minimum. | | Server error `invalid_argument` on a top-up | Amount outside the range | Ask for an amount inside the displayed range. | --- # Saved payment methods > **Where in the portal:** Billing › Settings (`/billing?settings=1`). **Where in the admin:** Customers › List › a customer › Overview (`/customers/`), card **Payment**, shows the saved method and the auto top-up history. ## What it does A saved payment method lets NexCMP charge a customer **without them being present**, so their services never stop because the wallet ran out. It is used only by [automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/). Two kinds exist: a **card** saved at Stripe (including cards used through Apple Pay, Google Pay or Link when Stripe returns a reusable card) and a **PayPal account** stored in the PayPal vault. ## Requirements | Item | Requirement | |---|---| | Channels | [Stripe](https://nexcmp.com/docs/billing/stripe/) and/or [PayPal](https://nexcmp.com/docs/billing/paypal/) configured and enabled. | | Site | The main site, or a reseller site whose payment channel is collected by the platform. A reseller site that uses its own Stripe account cannot save cards. | | Customer | A customer account that made a top-up with the card or PayPal. | ## How a method gets saved 1. The customer opens **Billing**, clicks **Deposit** and pays with **Card** or **PayPal**. 2. The payment is created so that the method can be reused later: Stripe stores the card with the customer at Stripe (off-session use), PayPal stores a vault token. NexCMP keeps only references (a Stripe payment-method ID and customer ID, or the PayPal vault ID) and a display label such as `visa •••• 4242` or the PayPal e-mail. **Card numbers never touch your server.** 3. When the payment completes, [automatic top-up](https://nexcmp.com/docs/billing/auto-top-up/) is switched on once if the customer never chose settings before. There is no extra tick box or message on screen. The server records the time, the customer's IP address and the method as evidence. 4. Next time, the deposit screen offers **Saved card ...** in the card dropdown, and **Saved PayPal ...** for PayPal. ## Manage and remove 1. In the portal go to **Billing** and click **Settings**. The panel shows the saved methods (Stripe card and/or PayPal) and the automatic top-up options. 2. To remove a card click **Remove card** and confirm. Stripe detaches the card and **auto top-up is turned off**. 3. To remove PayPal click **Remove saved PayPal** and confirm. ### Admin view Open the customer in **Customers › List**. The **Payment** card on the **Overview** tab shows the saved method labels (**Saved cards**), the gateway used for automatic charges, the stored evidence of consent (time, IP, method) and the last 20 automatic charge attempts with their status and reason (**Auto top-up history**). ## Verify 1. As a test customer, deposit the minimum by card. After the payment completes, open **Billing › Settings**: the card and the auto top-up switch (on) are visible. 2. Start another deposit: the dropdown offers the saved card. 3. In the admin, the customer's **Payment** card shows the same card label. ## Limits & notes - **Stripe Link and wallets.** Link, Apple Pay and Google Pay are Stripe features. NexCMP does not store anything Link-specific; it only keeps the card reference Stripe returns. - **PayPal.** Every PayPal top-up the customer starts needs a click; saved PayPal is used only by automatic top-up, never by other features. - **Reseller sites.** If a reseller uses its own Stripe or PayPal account, saved methods are hidden and automatic top-up is unavailable for that site. - **Disputes.** A dispute on any payment blocks the account and stops its prepaid services; see [Stripe](https://nexcmp.com/docs/billing/stripe/). - **Removing the last method** turns automatic top-up off; the customer keeps the wallet balance. - **Evidence.** The consent record is written by the server (not editable by the customer) and is kept for dispute handling. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Customer sees no Settings button or saved card | Site uses a reseller-owned payment channel, or no card payment yet | Make a card or PayPal top-up through the platform channel. | | "Saved card" missing after a Stripe payment | The payment was made with a method that cannot be reused (for example Alipay) | Pay again with a card. | | PayPal: "cannot save this account yet" | PayPal did not return a vault token | Continue with a one-time payment, or try another PayPal account. | | Auto top-up stopped after card removal | Expected: removing the card disables it | Save a new method with the next deposit. | --- # Auto top-up > **Where in the admin:** Finance › Payment channels › Fees & limits (`/payments/fees`), card **Wallet & top-up (Billing › Policy)**, section **Auto top-up (Stripe / PayPal)**. **Where in the portal:** Billing › Settings (`/billing?settings=1`). ## What it does When a customer's wallet balance falls too low, NexCMP charges their [saved payment method](https://nexcmp.com/docs/billing/saved-payment-methods/) and credits the wallet, so running services are not suspended for lack of funds. A background job checks all enabled customers **every 5 minutes**. ## Requirements | Item | Requirement | |---|---| | Channel | [Stripe](https://nexcmp.com/docs/billing/stripe/) and/or [PayPal](https://nexcmp.com/docs/billing/paypal/) configured. | | Saved method | The customer has a saved card or PayPal account. | | Site | Main site, or a reseller site whose Stripe/PayPal channel is collected by the platform. | | Global switch | **Auto top-up enabled** is on (default on). | ## How it works After a customer's **first Stripe or PayPal top-up** the server saves the payment method and turns automatic top-up on in **By usage** mode — only if that customer never set auto top-up themselves. There is no extra tick box on the deposit screen; the server records the time, IP address and method as evidence. If the customer turns auto top-up off later, it is never switched on again by itself. | Mode | Trigger (when the balance is below ...) | Amount charged | |---|---|---| | **By usage** (default after the first top-up) | the cost of the next 24 hours of running services (hourly VMs plus prepaid renewals due in 24 hours), but never below **$5** | the 24-hour cost rounded up to a whole dollar, at least the [minimum top-up](https://nexcmp.com/docs/billing/minimum-top-up/), at most the maximum top-up and the daily cap | | **Fixed amount** | the customer's own threshold ($1 to $500) | the customer's own amount (from the minimum top-up to $1,000, and larger than the threshold) | With no running service in **By usage** mode, nothing is charged. The balance compared with the threshold is the **real money in the wallet**. Service credits from promotion codes are kept separately and do not count (see [Promotions](https://nexcmp.com/docs/billing/promotions/)). The fee is handled like a normal top-up: with **Customer pays top-up fee** on, the card is charged amount plus fee and the wallet gets the full amount; with it off, the wallet gets the amount minus the fee. ## Configure the global limits 1. Open **Finance › Payment channels › Fees & limits** (`/payments/fees`). 2. In **Auto top-up (Stripe / PayPal)** set: | Setting | Default | Range | Meaning | |---|---|---|---| | Auto top-up enabled | On | on or off | Off stops every background charge immediately. | | Default auto top-up threshold (USD, fixed mode fallback) | $5.00 | $0 to $10,000 | Used only as the starting threshold in fixed mode. | | Max auto top-ups per 24 h per customer | 3 | 1 to 50 | Counts successful and pending attempts. | | Auto top-up amount cap per 24 h per customer (USD) | $500.00 | $0 to $1,000,000 | Total of automatic charges in a rolling day. | 3. Click **Save Wallet & top-up**. ![Auto top-up section of the Fees and limits page with the global switch and daily limits](https://nexcmp.com/docs/images/billing/auto-top-up-01-limits.webp) ## What the customer sees 1. In the portal the customer opens **Billing** and clicks **Settings**. 2. The panel shows the saved method(s), the on/off switch, the mode (**By usage** or **Fixed amount**), the estimated current usage and the next charge, and the **Recent automatic top-ups**. 3. Switching to **Fixed amount** lets them enter the threshold and the amount. Turning the switch off stops charges. ![Customer billing settings with auto top-up in By usage mode and the recent automatic top-ups](https://nexcmp.com/docs/images/billing/auto-top-up-02-portal.webp) ## Verify 1. As a test customer, make a first top-up with a test card ([Stripe](https://nexcmp.com/docs/billing/stripe/#verify)). Open **Billing › Settings**: auto top-up is on. 2. Run a small VM so that there is usage, and reduce the balance below the threshold (for example with **Customers › customer › Credit / debit**). 3. Within about 5 minutes a charge is made. In the customer's **Overview › Payment** card, **Auto top-up history** shows the attempt with status *Done*, and a *Top-up received* notification is sent. ## Limits & notes - **One charge per drop.** After a charge the job waits until the balance has risen back above the threshold or the waiting time ends: 1 hour after a success, 24 hours after a pending charge, 30 minutes after a system error. - **Daily limits.** When either daily limit is reached, no charge is made and a *Daily limit reached* line is written (at most once every 6 hours). - **Failures.** A declined card, expired card or insufficient funds counts as a card failure. The job retries after 1 hour, then 6 hours, then 24 hours. **After 3 consecutive card failures auto top-up is switched off** and the customer is notified (*Auto top-up failed*). A payment that needs the customer's confirmation (for example 3-D Secure) is not counted as a failure; the customer is told to confirm it. - **No double charges.** Each charge is claimed atomically per drop below the threshold and uses an idempotency key at the gateway. - **Removing the card** turns auto top-up off. - **Resellers.** If a reseller uses its own payment channel, the customer cannot enable auto top-up (HTTP 403 `feature_disabled`). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Customer cannot switch it on: "Top up once with a card or PayPal" | No saved method | Make one card or PayPal top-up first. | | Error `no_card` (422) | The chosen gateway has no saved method | Pick the gateway that has one, or save a method. | | Error `invalid_argument` on amount | Fixed mode outside the allowed ranges | Threshold $1–$500, amount between the minimum top-up and $1,000, amount greater than the threshold. | | Nothing is charged although the balance is low | Global switch off, no running service in By usage mode, daily limit reached, or the previous charge is still waiting | Check the switch, the history and the limits. | | "N consecutive failed attempts — auto top-up switches off after 3" | Card keeps failing | Customer replaces the card with a new deposit. | | Status *Needs your confirmation* | The bank requires 3-D Secure | The customer completes a normal top-up once. | --- # Invoices and ledger > **Where in the admin:** Finance › Billing (`/billing`) with the tabs Overview, Transactions (`/billing/ledger`), Invoices (`/billing/invoices`), Top-ups (`/billing/deposits`), Payouts (`/billing/payouts`), Vouchers (`/billing/vouchers`) and Reports (`/billing/reports`). **Where in the portal:** Billing (`/billing`). ## What it does Every amount in NexCMP is stored as USD cents and recorded once. The key idea: the **wallet ledger** is the source of truth for balances; invoices, top-up payments and reports are views built from it. The ledger is **append-only** — a mistake is fixed by adding a reversing entry, never by editing history. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Seller details | Set the legal name, tax ID, address, contact e-mail and the invoice number prefix in **System › Settings › General**, tab **Seller & invoicing** (`/settings?tab=seller`), card **Seller details (printed on invoices)**. | | Dependencies | [Payment channels](https://nexcmp.com/docs/billing/payment-channels/) for money coming in. | ## The pieces | Admin tab | What it lists | Typical use | |---|---|---| | **Overview** | Revenue today and this month, ledger totals by kind (deposit, charge, refund, adjustment, voucher), invoices by status, pending refunds, shortcuts, spending per service in the last 30 days | Daily check | | **Transactions** | The wallet ledger, one line per movement | Explain a customer's balance | | **Invoices** | Invoices, and a second view **Orders** with every purchase of every service | Find a document, resend, mark paid | | **Top-ups** | Payments received through the gateways | Check a payment, refund it | | **Payouts** | Card refunds, affiliate payouts, reseller withdrawals | Approve money going out | | **Vouchers** | Top-up codes | See [Promotions](https://nexcmp.com/docs/billing/promotions/) | | **Reports** | Revenue, by service, by reseller | Accounting | ### Transactions (ledger) Columns: time, customer, **kind**, service, amount (signed), VM, reference, actor, note. Filters: kind, service, customer ID, site (when you have resellers), date range, and a VM link. | Kind | Meaning | |---|---| | `deposit` | Top-up from a gateway | | `charge` | Money taken for a service (order, renewal, hourly usage ...) | | `refund` | Money returned to the wallet | | `adjust` | Administrator credit or debit, and also top-up codes (reference starting `vch:`) — the overview shows vouchers as their own line | Every row has a reference such as `dep:stripe:`; the same reference can never be written twice, which is why a repeated webhook cannot credit twice. Use **Customers › List › customer › Credit / debit** to add a manual adjustment: a reason is mandatory, a positive amount also tries to unlock VMs locked for lack of funds, and the action is confirmed in two steps. ![Transactions tab showing wallet ledger rows with kind, service, amount and reference](https://nexcmp.com/docs/images/billing/invoices-and-ledger-01-ledger.webp) ### Invoices An invoice is created automatically when a customer buys or renews something paid from the wallet, or for a top-up. Numbers look like `-<000001>` (for example `NX202610-000042`); reseller sites use their own prefix and sequence. Existing numbers never change when you change the prefix. | Field | Values | |---|---| | Kind | Receipt, Renewal, Top-up, Manual, Refund | | Status | Unpaid, Paid, Overdue, Cancelled, Refunded | Actions on a row: | Action | Effect | |---|---| | **View** / **Print** | Opens the invoice; the portal can also print or save as PDF. | | **Mark paid** (unpaid or overdue only) | Marks it paid **without moving wallet money** — use it when the customer paid you outside NexCMP. A reason is required. For a renewal invoice it also extends and unlocks the locked VM. | | **Cancel** (unpaid or overdue only) | Cancels the invoice. A reason is required. | | **Resend** | E-mails the invoice again. | **Create manual invoice** (button at the top of the tab): customer ID, 1 to 50 lines (description, quantity, unit price), optional VM ID, optional due-in days (0 to 365, default 7), optional note. It is created as unpaid; the customer sees it in **Billing › Invoice History**. Unpaid invoices of services are **settled automatically from the wallet** as soon as there is money, so the customer's action is simply to top up. ### Top-ups (payments) One row per attempt: time, customer, **gateway and method**, kind (top-up or auto top-up), status (pending, completed, failed, refunded, disputed, canceled), what the customer paid, the fee, what went to the wallet, the gateway reference and completion time. Completed Stripe and PayPal payments have a **Refund** action. Use the Gateway events pages to see webhooks ([Payment channels](https://nexcmp.com/docs/billing/payment-channels/)). ### Payouts Three sub-tabs: **Card refunds** (approve or reject refund requests; the request is created from a completed payment), **Affiliate payouts** (commission withdrawals) and **Reseller withdrawals**. Every decision is audited. ### Reports **Revenue** (30-day trend, month by month, top customers), **By service** and **By reseller**. ![Reports tab showing revenue KPIs and the thirty day trend](https://nexcmp.com/docs/images/billing/invoices-and-ledger-03-reports.webp) ## What the customer sees In **Billing** the customer has **Balance**, **Deposit**, **Redeem code**, **Settings** (auto top-up) and a history with tabs **Invoice History**, **Payment Order**, **Ledger** and **Orders** (every service in one list with filters by service and type). Each invoice has **View**, **Print** and **Save as PDF**; amounts are in USD, optionally shown in the display currency. ## Verify 1. Make a small top-up as a test customer. 2. **Transactions** shows a `deposit` line, **Top-ups** shows the payment as *Completed*, and **Overview** revenue increases. 3. Buy something: a `charge` line appears, and **Invoices** shows a *Receipt* or *Renewal* invoice with the seller details you configured. ## Limits & notes - **Append-only.** There is no way to edit or delete a ledger line; add an adjustment with a reason. - **Currency.** Amounts are USD; the display currency is cosmetic (see [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/)). - **Refund to card** is two steps (request, then approval under Payouts); USDT and bank transfers are not refunded to the source. - **Everything is audited** under System › Logs › Admin actions with the reason you entered. - **Orders vs invoices.** Hourly usage is not invoiced per hour; the Orders view groups it by day so you can still see it. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Customer's balance seems wrong | A charge, adjustment or voucher you did not expect | Filter **Transactions** by the customer ID and read the references and notes. | | "Invoice is already paid" (409) | Mark paid on a paid invoice | Nothing to do. | | Cannot cancel an invoice | Only unpaid or overdue invoices can be cancelled | For paid ones use a refund or an adjustment. | | Seller details missing on invoices | Not filled in | Complete **Seller details** in System › Settings › General › Seller & invoicing. | | Credit / debit refused | Amount is zero or over $100,000 | Use an amount between 1 cent and $100,000. | | A payment shows *Pending* for a long time | Webhook missing | See **Gateway events** and replay. | --- # Promotions > **Where in the admin:** Finance › Billing › Vouchers (`/billing/vouchers`). **Where in the portal:** Billing › Redeem code (also inside the Deposit drawer). ## What it does A **top-up code** (voucher) is a one-time-use secret you give to customers. Depending on how you create it, it can: | Type | What the customer gets | Where the value goes | |---|---|---| | **Bonus credit** for all services | Money added to the wallet | Wallet balance. Usable for anything. | | **Bonus credit** for chosen services | A service credit | A separate pocket that can only pay for those services. | | **Discount %** | A percentage off the first order of chosen services | Nothing is credited; the discount is applied at checkout. | Use them for campaigns, apology credits, partner deals or a first-order discount. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Customers | Customers redeem codes while signed in to the portal (not as a guest). By default the customer must have a verified e-mail. | | VND codes | A code created in VND converts to USD at redemption using the stored exchange rate (see [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/)). | ## Create a batch 1. Open **Finance › Billing › Vouchers** and click **Create vouchers**. 2. Fill in the form. Example values are in brackets. | Field | Notes | |---|---| | Batch name | 1–80 characters (`Spring welcome`). | | Target | **Main site clients**, **Single reseller's customers** (needs a list of customers) or **Reseller wallet**. | | Eligible users | **All in target audience** or **Specified** — one e-mail or customer ID per line, up to 1,000. For a reseller's customers it must be *Specified*. | | 1 code per target | With *Specified*, creates one code per listed customer. | | Code type | **Bonus credit** or **Discount %**. | | Applies to | Pick one or more services; empty = **All services**. | | Currency, Face value | USD from $1.00, or VND from 10,000 in multiples of 1,000 (bonus credit only). | | Discount (%) | 1 to 100 (Discount % only). | | Maximum discount cap (USD) | Required for Discount %, from $1.00. It is also the most you can lose per redemption. | | Minimum order (USD) | `0` = none. The first order must be at least this amount. | | Code count | 1 to 1,000 random codes. | | Uses per code | 1 to 100,000. `1` means one person per code; raise it for a shared code. | | Max uses per account | At least 1. For Discount % it must be `1`. | | Start time (optional), Expires | The expiry must be after today and after the start, at most 2 years ahead. | | Code prefix (0–6 chars) | Letters only; empty uses the site's prefix. | | Custom code (only when count = 1) | 6 to 32 characters A–Z and 0–9. Easy to share but easy to guess: keep the uses low. | | Require verified email | On by default. | | Internal note, Reason | The reason is mandatory and goes to the audit log. | 3. The form shows **Maximum exposure** (face value x codes x uses) so you know the worst case. Click **Generate codes**. 4. **Copy the codes now.** They are shown only once: NexCMP stores only a hash and the last 4 characters. Use **Copy all** or **Download CSV**, tick the confirmation and close the box. ## How each type behaves ### Wallet bonus (no service selected) The wallet is credited with an **adjustment** entry (reference `vch:...`). It is not refundable to a card, does not earn affiliate commission, does not create an invoice and counts as wallet balance for quotas. VND codes use the exchange rate at the moment of redemption. ### Service credit (services selected) - The credit sits in a **separate pocket**, can only pay charges of the chosen services (new orders, renewals, hourly or daily fees, upgrades) and is spent **before** the wallet. - It cannot be withdrawn, transferred or converted to money, and it expires with the code. - The ledger shows the full charge plus a matching credit line, so revenue per service stays correct while the wallet only drops by what the customer really paid. - It does **not** raise a [quota](https://nexcmp.com/docs/billing/quotas/) tier and is ignored by [auto top-up](https://nexcmp.com/docs/billing/auto-top-up/). - If a charge paid with credit or a discount is refunded, the part that the code paid is taken back from the wallet refund and returned to the credit pocket. ### Discount % 1. The customer enters the code: it becomes **activated**. A percentage code can be used only once per account. 2. The **first charge** of a selected service that is not an hourly fee, and at least the **Minimum order**, is discounted by the percentage, never more than the **Maximum discount cap**. 3. The discount is recalculated by the server at charge time; the amount in the browser is ignored. The redemption row is written when the code is actually used, with the real discount. 4. Hourly usage charges never use the code. ## What the customer does 1. Open **Billing** in the portal and click **Redeem code**, or use **Have a top-up code?** in the Deposit drawer. 2. Type the code (not case-sensitive; dashes optional) and click **Check**. The portal says what it gives, for example *Valid code: +$5.00*, *$5.00 credit for VPN* or *20% off voucher for Virtual Machines (up to $5.00)*, and *Valid for orders from $10.00*. 3. Click **Redeem** and confirm. Each code can be used once per account. Codes are tied to the site: a code from one site is "not valid" on another, with no hint that it exists elsewhere. Wrong guesses are throttled and, after several, the account is locked out for a few minutes ("N attempts left before a temporary lock"). ## Track and stop - **Batches** lists each batch with its status (*active*, *disabled*, *expired*, *used up*), redeemed count and exposure. Open a batch for the **Codes** (masked) and **Redemptions** tabs, and **Export CSV**. - **Redemptions** lists every redemption across batches; **Reseller quotas** shows how much of each reseller's monthly issuing limit is used; **Settings** switches reseller codes on or off and sets the default monthly limit. - **Disable batch** stops all unused codes; redemptions already made stay. A single code can also be disabled. ## Verify 1. Create a batch of 1 code: Discount % 20, cap $5, minimum order $10, service Virtual Machines. 2. As a test customer redeem it. The portal says it is activated. 3. Order a VM that costs at least $10 (monthly cycle): the invoice shows the discount (at most $5). 4. In the batch, the code shows 1 redemption with the real discount. ## Limits & notes - Codes are tied to the **audience** and the **site** at creation; they cannot be moved later. - Resellers can issue codes to their own customers within a monthly exposure limit; see [Resellers](https://nexcmp.com/docs/customers/resellers/). - The sidebar promo slides for customers (**Marketing › Promo slides**) are a separate feature and do not give any discount. - Because the code in plain text is never stored, a lost batch cannot be recovered: disable it and create a new one. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Percent codes: one use per account" (`percent_one_per_account`) | Max uses per account above 1 for Discount % | Set it to `1`. | | Form says the cap is required | Discount % without Maximum discount cap | Enter at least $1.00. | | Customers not found | An e-mail or ID in the list does not exist | Fix the list (the error names the unknown entries). | | Customer says "invalid code" | Wrong site, expired, disabled, used up, not the targeted customer, or e-mail not verified | Check the batch and the customer. | | Credit was not used | The charge belongs to another service | Credit only pays the services chosen on the batch. | | Discount not applied | Order below the minimum, hourly charge, or the code was activated for another service | Place a qualifying first order. | | Customer locked out of redeeming | Too many wrong attempts | Wait for the lock to expire. | --- # Quotas > **Where in the admin:** Services › Virtual Machines › Policy › Quota tab (`/vm/policy?tab=quota`). Per customer: Customers › List › a customer › Overview (`/customers/`), card **Quota**. **Where in the portal:** Client › Quotas (`/client/quotas`). ## What it does Quotas protect you from abuse by new accounts: the more real money a customer has put into the wallet, the more resources they may create. There are two layers: - **VM and Kubernetes resources**: total vCPU, RAM and disk, and the number of VMs. Tiers are your fixed plans. - **Service quotas**: how many IPs, clusters, VPCs, GB of storage and so on, in five tiers (L0 to L4) driven by the money deposited. Quotas are checked **before** anything is charged. A request over the limit is refused with HTTP 403 `quota_exceeded` and a message telling the customer how much to top up. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Switch | **Enable quota** (`quota_enabled`) is **off** by default. Nothing below applies until you turn it on. | | Fixed plans | At least one non-custom plan (see [Plans](https://nexcmp.com/docs/catalog/plans/)) — the resource tiers are your fixed plans. Without any plan only the VM-count rule applies. | | Dependencies | Service quota rows exist only for the products that are enabled in your installation (VPN, IPv6 proxy, Kubernetes, Private Cloud, Images, Object Storage, Dedicated). | ## How the basis is calculated NexCMP does not look at card payments alone. The **basis** is the customer's **posted wallet balance**: gateway top-ups, administrator credits (**Credit / debit**) and redeemed wallet vouchers all count once, through the wallet ledger. Order discounts and service credits from promotion codes are not wallet money and do not count. The highest basis a customer ever reached is **kept** (per site) — spending or refunds never lower an already unlocked tier. ### VM and Kubernetes resources 1. The tiers are the site's fixed plans, sorted by the cost of running them for 24 hours. 2. A customer is on the highest tier whose 24-hour cost is not above their basis. The limit is that plan's vCPU, RAM and disk. A customer who cannot afford even the cheapest plan still gets the cheapest plan's resources. 3. The usage counted against the limit is every live VM (including locked ones), purchased data volumes (even when detached) and Kubernetes workers and control planes (workers also count as VMs). 4. **VM count:** up to **Max VMs at 24h tier** (default 2). From the next VM on, the customer's *current* wallet balance must cover **30 days (720 hours)** of all their existing VMs plus the new one, up to **Max VMs when meeting 30-day threshold** (default 10). 5. **Creating a VM** also needs a balance of at least **N hours** of that VM (**Create VM: balance ≥ N hours**, default 2) and the platform minimum balance. 6. Making a VM smaller is never blocked. Only the growing dimension is checked, so a customer who is above the disk limit can still add vCPU if the vCPU limit allows. ## Configure 1. Open **Services › Virtual Machines › Policy** and select the **Quota** tab. 2. Switch on **Enable quota**. 3. Review the three numbers (all editable): | Field | Default | Range | |---|---|---| | Create VM: balance ≥ N hours | 2 | 1 to 720 | | Max VMs at 24h tier | 2 | 1 to 100 | | Max VMs when meeting 30-day threshold | 10 | 1 to 1000 (not below the previous field) | 4. In **Tier-based service quotas** edit the table (below). 5. Click **Save**. NexCMP asks for a **reason**, which is written to the audit log together with the old and new values. The change applies to all customers. ![Quota tab with the enable switch, the VM limits and the tier-based service quota table](https://nexcmp.com/docs/images/billing/quotas-01-policy.webp) ### Service quota table The table has five tiers. The header cells **L0** to **L4** hold the **deposit threshold in cents**: the defaults `0, 1000, 5000, 20000, 100000` mean L0 for a new account, L1 from $10, L2 from $50, L3 from $200 and L4 from $1,000. The first threshold must stay `0`. Each row sets the quota of one key per tier; **-1 means unlimited**. | Key | Meaning | L0 | L1 | L2 | L3 | L4 | |---|---|---|---|---|---|---| | `vpn.ips` | VPN IPs | 2 | 10 | 30 | 100 | 500 | | `proxy6.ips` | IPv6 proxy IPs | 500 | 2000 | 10000 | 50000 | -1 | | `dedicated.servers` | Bare-metal servers | 1 | 2 | 5 | 20 | 50 | | `s3.storage_gb` | Object Storage GB allocated | 2048 | 4096 | 10240 | 51200 | -1 | | `k8s.clusters` | Kubernetes clusters | 1 | 2 | 5 | 10 | 30 | | `k8s.workers` | Kubernetes worker nodes | 3 | 9 | 18 | 50 | 200 | | `vpc.count` | Private Cloud networks | 1 | 2 | 5 | 10 | 30 | | `images.gb` | Custom image storage GB | 50 | 100 | 200 | 500 | 2000 | The table also lists a few rows for products that are not part of this release; leave them unchanged. A key whose product is turned off is hidden and never enforced. A value must be `-1` or between `0` and `10,000,000`. Service quotas follow the **funds basis**, like the VM tiers: the highest wallet balance reached, not the total of card payments. Reseller customers use the same defaults and the reseller can only view them. ## Per-customer overrides 1. Open **Customers › List**, then the customer. In **Overview** find the card **Quota**. 2. **VM resources:** click **Quota override** and choose one of - **By top-up balance (remove override)** — default behaviour; - **Unlimited (including VM count)** — removes every cap for this customer; - a **plan tier** — fixes the resource ceiling to that plan; the VM-count rule still applies. Enter a reason. Platform administrators can set any tier; an override set by a reseller cannot exceed the customer's retained basis. 3. **Services:** the table under *Service quotas* shows each key with *In use*, the effective limit and its *Source* (tier or override). Click **Edit** on a row, type the **New quota** (`-1` = unlimited, empty = back to the tier default) and a reason. ![Customer Quota card with the tier, the resource usage, and the service quota table with an override](https://nexcmp.com/docs/images/billing/quotas-02-customer.webp) Every override is recorded in the audit log with the administrator, reason and before/after values. ## What the customer sees **Client › Quotas** (`/client/quotas`) shows the customer's **current tier**, the used and total vCPU, RAM, disk and VM count (split between VMs and Kubernetes), the **eligible wallet balance**, the **held resource basis** (it does not decrease when they spend), and how much to top up for the next tier. A slider lets them pick a target tier and go straight to the deposit screen. The **Service Quotas** card lists each service with the quota, the used amount, their tier (L0 to L4), how much more to deposit for the next tier, and a **Request increase** button that opens a support ticket draft. A **Quota policy** button explains the rules. ![Portal Quotas page with the current tier, the resource usage and the service quotas](https://nexcmp.com/docs/images/billing/quotas-03-portal.webp) ## Verify 1. Turn on **Enable quota** and use a fresh test customer with a $0 balance. 2. Try to create a VM bigger than your cheapest plan. The request is refused with `quota_exceeded` and the message "Exceeds the resources of tier ... Top up $X more to unlock tier ...". 3. Top up the amount shown. The tier changes at once and the same request succeeds. 4. Buy VPN IPs above the L0 limit: HTTP 403 `quota_exceeded` with `"scope":"service"` and a link to `/client/quotas`. 5. In the customer's Quota card, **Source** shows *tier*; set an override and see it change to *override*. ## Limits & notes - **Off means off.** With `quota_enabled` off the old rule `new_account_max_vms` (**Services › Virtual Machines › Policy › Sales**) limits new accounts to 2 VMs until their deposits reach $10. When the quota is on, that rule no longer applies. - **Admin-created VMs are not blocked.** - **Errors fail closed.** If the quota state cannot be read, the request is refused with `quota_unavailable` rather than allowed. - **API, CLI and MCP** requests use the same handlers, so the same limits apply. - **Promotion credit** does not raise a tier (see [Promotions](https://nexcmp.com/docs/billing/promotions/)). - **The tier thresholds are cents**, not dollars. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `quota_exceeded`, reason `resources` | The new total vCPU/RAM/disk is above the tier | Top up the amount in the message or set an override. | | `quota_exceeded`, reason `vm_30d` | More than the 24h-tier VM count and the wallet does not yet cover 30 days of the fleet | Top up the amount shown. | | `quota_exceeded`, reason `vm_max` | The absolute VM cap is reached | Raise **Max VMs when meeting 30-day threshold** or use an override. | | `quota_exceeded`, `scope: service` | A per-service limit is reached | Raise the table value or override that key for the customer. | | `quota_unavailable` | The quota state could not be verified | Retry; check the server log if it persists. | | "quota_max_vms_30d must be >= quota_max_vms_24h" on save | Values reversed | Make the 30-day number at least the 24-hour number. | | Customer says the tier dropped | It does not drop; compare **Held resource basis** with the plan cost | Check the plan price list: a plan price increase can move the thresholds. | --- # Managing customers > **Where in the admin:** Customers › Customers › List (`/customers`), Deletion requests (`/customers/requests`), Service transfers (`/customers/transfers`) and API keys (`/customers/apikeys`). ## What it does The Customers area is where support staff look up an account, see its wallet, services, tickets and sign-in history, and act on it: lock, unlock, verify e-mail, reset the password, add or remove credit, impersonate, create a VM or close the account. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. Every write action asks for a reason and is written to the audit log (System › Logs › Admin actions). | | E-mail | Password reset and invitation e-mails need a working [SMTP configuration](https://nexcmp.com/docs/administration/smtp-and-emails/). | ## The customer list Open **Customers › List**. Customers are accounts of your main site; accounts of resellers appear too and can be filtered by site. ![Customer list with the filters and the country, referral source, balance and VM count columns](https://nexcmp.com/docs/images/customers/managing-customers-01-list.webp) | Column | Meaning | |---|---| | **Email** (and name) | Click to open the customer. | | **State** | *Active*, *Locked* or *Deleted*, plus *verified* or *unverified* e-mail. Hover over a lock tag to read the reason. | | **Login** | Sign-in methods the customer has: password, Google, GitHub, passkey. | | **Country** | Flag and name derived from the **IP address**: the country of the registration IP, or, when that is unknown, of the last sign-in IP. The tooltip says which one was used. | | **Referral source** | *Referral* with a link to the referrer and the referral code when the customer signed up through an affiliate link; otherwise "Not recorded". | | **Balance**, **VMs** | Wallet balance (negative values in red) and the number of live VMs. | | **IP registered / last** | The IP used at sign-up and at the last sign-in with a two-letter country. Use it to spot several accounts from one address. | | **Tenant** | The customer's internal tenant ID. | | **Created**, **Last login** | Relative times. | | **Site** | Shown when you have resellers. | **Search** accepts e-mail, name or IP. **Filters:** state (all, active, locked, deleted), login method (any, Google, GitHub, passkey, no password yet) and site. Filters are kept in the URL, so a filtered list can be bookmarked or shared with a colleague. **Sorting is global.** Clicking the header of *Email*, *State*, *Balance*, *VMs*, *Created* or *Last login* sorts the **whole** customer base on the server, not only the page you are looking at, and the page number goes back to 1. **The list updates by itself.** It refreshes every few seconds while you look at it (it pauses while a dialog is open or while you type), so new sign-ups and balance changes appear without reloading. The customer page refreshes about every 30 seconds while visible; each tab loads its own data when first opened. ### Create a customer 1. Click **Create customer**. 2. Enter the **e-mail**, an optional **display name**, an optional **password** (empty = the customer sets it through the invitation link) and choose whether to **send the invitation e-mail**. 3. Confirm. A hand-made account counts as verified when you send the invitation. ## A customer's page Click a row. The page has eight tabs; the selected tab is kept in the URL so you can bookmark it. Tabs load when first opened and retain their state when you switch away. | Tab | Content | |---|---| | **Overview** | Identity, registration and last sign-in information, wallet and service summary | | **Services** | One table for the customer's services, with type and state filters, search, server-side sorting and pagination | | **Billing** | Wallet and credit, ledger, invoices, payments, refunds, saved payment method and auto top-up | | **Quota** | Resource limits and overrides; see [Quotas](https://nexcmp.com/docs/billing/quotas/) | | **Security** | Sessions, authentication, sign-in methods, sign-in history and API keys; see [API keys](https://nexcmp.com/docs/developers/api-keys/) | | **Support** | Recent tickets | | **Affiliate** | Referral programme status | | **Log** | Audit and activity history | Action buttons in the header (hidden for deleted accounts): | Button | What it does | |---|---| | **Lock account** / **Unlock** | Blocks sign-in and API access. Optionally also stops all VMs (without deleting them). Prepaid services that are not VMs (Kubernetes, Private Cloud, VPN and so on) are stopped as well. VMs locked by an administrator must be opened separately on the VM page. | | **Verify email** | Marks the e-mail as verified without the customer clicking a link. | | **Send reset link** | E-mails a password-reset link (valid 1 hour, single use). If SMTP is not configured the link is shown once so you can send it yourself. An administrator never sees the password. | | **Impersonate** | Signs in as the customer for one hour. See [Log in as a customer](https://nexcmp.com/docs/customers/login-as/). | | **Credit / debit** | Adds or removes wallet money by a ledger adjustment. See [Invoices and ledger](https://nexcmp.com/docs/billing/invoices-and-ledger/). | | **Revoke sessions** | Signs the customer out everywhere; app tokens stop working. | | **Create VM** | Creates a VM for the customer. | | **Soft delete** | Closes the account. You must type the e-mail to confirm. | ### Manage an individual service Open the customer's **Services** tab, find a service by name, ID, IP or configuration, and use its row actions. The same actions are available in the service's own admin list. The dialog only offers actions supported by that service and its current state. | Action | Effect | |---|---| | **Lock** | Stops the service while keeping its data. The customer sees it as locked and cannot operate it. Topping up does not remove an administrator's lock; automatic renewal is blocked while locked. | | **Unlock** | Reverses the administrator's lock. It does not remove an unrelated abuse or other protection lock. | | **Cancel without refund** | Cancels the service without a refund ledger entry. The confirmation warns that cancellation cannot be undone. | | **Cancel and refund** | Shows the original payments, their wallet/credit allocation, previous refunds and the maximum refundable amount. Review the amount before confirming. | Enter a reason for every action. The reason is for administrators; the customer receives a neutral service event or notification. Refunded wallet money returns to the wallet, and credit or promotional allocations return to credit. This action does not send money back through Stripe, bank transfer or PayPal. For several services of the same type, select up to 50 rows and choose a bulk action. The result lists success or failure for each row. Bulk refunds use each service's suggested amount; services whose suggested refund is zero are skipped in refund mode and need individual review or cancellation without refund. DDoS Shield, disks and backups are read-only rows here. Bandwidth add-ons follow their parent service. AI Models access can be locked, but it has no subscription to cancel. To lock a rented Bare Metal server, act on its subscription rather than its order. ### Closing an account (soft delete) The account is set to *deleted*, blocked, its sessions are revoked and its referral commissions that were not yet paid are reversed. Invoices and the ledger stay for accounting. The action is refused while the customer still has **any live service** (VM, Kubernetes, Private Cloud, Dedicated, VPN, IPv6 proxy), an S3 account or unsettled S3 usage. Delete those first. ## Deletion requests A customer can ask for deletion from their account page. Requests appear under **Customers › Deletion requests** (default filter: pending) with the customer, reason, number of live VMs, balance and request time. - **Approve deletion** is possible only when the customer has no live VM. Type the e-mail to confirm. The account is soft-deleted; **the remaining wallet balance is not refunded automatically** — create a refund first if you owe it. - **Reject** needs a reason. ## Service transfers **Customers › Service transfers** lists requests to move a **VM or a VPN subscription** from one account to another account **on the same site**. - The screen is read-and-create-only: you can create a request on behalf of a customer or cancel one. There is deliberately **no** button to approve it for the owner or the recipient. - Flow: the owner confirms through a single-use e-mail link (valid 30 minutes, with TOTP if enabled), then the recipient signs in and accepts (the recipient takes over the payment obligations). A request not accepted within 7 days expires. - Rules: the service must be active, with no unpaid invoice and no running job; the recipient must be an active customer with a verified e-mail on the same site. A VM transfer removes the SSH key and turns auto-renew off; a VPN transfer changes the connection passwords. - Invoices already issued stay with the previous owner; the new owner can read the service's billing history with the previous owner's data hidden. - Customers can create their own requests under **Transfers** in the portal (5 requests per hour and 3 open at a time). ## Verify 1. Create a customer with an invitation e-mail. The new account appears at the top of the list within a few seconds. 2. Sort by **Balance**: the order covers all customers. 3. Lock the customer: the state tag turns to *Locked* and the customer can no longer sign in. Unlock it. ## Limits & notes - The country column is a guide based on IP, not proof of residence. Private or unknown addresses give no country. - Locking an account does not refund anything and does not delete VMs unless you also choose to stop them. - Unlocking the account does not unlock VMs that an administrator locked; open them separately on the VM page. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Customer still has live services ... stop/delete first" | The customer has services or an S3 account | Remove them, then soft delete. | | Delete approval says "N VMs alive" | Same reason | Delete the VMs first. | | Reset link shown instead of "e-mail queued" | SMTP not configured | Configure e-mail, or send the link yourself. | | Cannot remove a sign-in method (`last_login_method`) | It is the customer's only way to sign in | Add another method first. | | Lock does not stop prepaid services | Stop errors were reported | Read the message after locking; the system retries stopping automatically. | | A transfer cannot be created | Unpaid invoice, a running job, recipient on another site or unverified | Fix the condition and retry. | --- # Log in as a customer > **Where in the admin:** Customers › Customers › List › a customer › header button **Impersonate** (`/customers/`). ## What it does **Impersonate** (login-as) lets a support administrator see the portal exactly as one customer sees it, to reproduce a problem or help with an order. The session lasts one hour and is clearly separated from a normal session: anything that could give lasting access to the account is blocked, and every use is logged. ## Requirements | Item | Requirement | |---|---| | Role | An administrator signed in to the admin console. | | Customer | The account must be **active**. A locked or deleted account answers "account is not active" (HTTP 409). | | Reason | A reason is mandatory; it is written to the audit log. | | Browser | Allow pop-ups for the admin address: the portal opens in a new window. | ## Steps 1. Open **Customers › List** and click the customer. 2. Click **Impersonate**. A dialog explains that the customer's site opens as the customer for one hour and that every action is audited. 3. Type the **reason** (for example "Ticket 1234 — cannot create VM") and click **Open app**. 4. A new window opens on the customer's site and shows the dashboard with `?impersonated=1`. If the browser blocked the window, allow pop-ups and click again. ![Impersonate dialog with the reason field filled in](https://nexcmp.com/docs/images/customers/login-as-01-dialog.webp) For a customer of a reseller, the window opens on the **reseller's own domain**, because the link only works on the customer's own site. ## How it works 1. The admin action creates a **single-use token** valid for one hour and writes `customer.login_as` (administrator, IP, reason, site) to the audit log. 2. The new window opens `https:///v1/portal/impersonate/`. A first request only shows a small page and does **not** consume the token (so browser prefetching cannot burn it); the page then confirms with a POST. 3. The POST consumes the token. A second use, or use after one hour, answers "The impersonation link has expired or was already used". 4. NexCMP creates a customer session that lasts **one hour** (a normal session lasts 30 days), records a sign-in entry with the method `impersonate`, and logs `customer.login_as.used`. 5. The token works only on the customer's own site; on another host it answers 404. ## What is blocked in an impersonated session Anything that gives access beyond the hour, or changes the customer's own security, returns HTTP 403 `impersonate_forbidden`: | Blocked | Why | |---|---| | Changing the password, TOTP, linking or unlinking Google/GitHub, editing passkeys | Customer's own credentials | | Deleting the account | Irreversible | | Creating or editing **API keys** | Long-lived access | | Approving a CLI, desktop or MCP login (device link) | Long-lived access | | Creating Kubernetes access, S3 keys, SSH keys, root passwords, console share links, VPN or proxy credentials | Secrets that outlive the session | | Redeeming top-up codes | Money campaigns | | Withdrawing or changing the payout of affiliate commission | Money leaves the system | | Editing AI keys, budgets or alert e-mails | Customer configuration | Everything else works: you can read the pages, create or manage VMs, place orders and so on. Actions run with the customer's identity and spend the customer's money; the audit entries above are what show that an administrator was behind the session. ## Verify 1. Impersonate a test customer. The portal opens and shows that customer's data. 2. Try to change the password: you get "forbidden" (impersonation). 3. In **System › Logs › Admin actions**, find `customer.login_as` with your name, the reason and the site, followed by `customer.login_as.used`. 4. Close the window, reopen the same link: "link expired or already used". ## Limits & notes - The one-hour limit is fixed (3600 seconds). To continue, impersonate again. - Prefer reading and reproducing; avoid actions that spend money unless the customer asked you to. - Revoke the session early with **Revoke sessions** on the customer page (this signs the customer out too). - Resellers have their own **Sign in to partner console** impersonation (60 minutes) from Resellers; see [Resellers](https://nexcmp.com/docs/customers/resellers/). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Account is not active" | Account locked or deleted | Unlock it first, or look at the customer page instead. | | Browser blocks the window | Pop-up blocker | Allow pop-ups for the admin address and try again. | | "Link expired or already used" | One-hour limit or reuse | Impersonate again. | | Opens "Not found" | The link was used on the wrong host | Use the window the dialog opened; do not paste the link elsewhere. | | An action says "forbidden" | Blocked in impersonated sessions | Ask the customer to do it, or use the admin screens. | --- # Resellers (white-label) > **Where in the admin:** Customers › Resellers › List (`/resellers`), Domains & SSL (`/resellers/domains`), Permissions & wholesale (`/resellers/entitlements`), Defaults (`/resellers/settings`). Reseller slots: System › Admin › License & updates (`/license`). ## What it does A **reseller** gets a branded copy of the customer portal on **their own domain**, with their own customers, wallet, prices and (optionally) payment channels. They buy from you at a wholesale price and sell at their own price. Their customers never see your brand, and the reseller domain can never open your admin console. The reseller runs their business from the **partner console** at `https://partner.`. ## Requirements | Item | Requirement | |---|---| | License | One **reseller domain slot** on your license for each custom reseller domain. The license page shows `used/max` slots and a link to buy more. Without a free slot, adding a domain fails with "Out of license reseller domain slots". | | DNS | Each reseller domain must be pointed at your server (see below). | | Web server and TLS | The host name must reach the NexCMP panel through your reverse proxy with a valid certificate for that name. | | E-mail | SMTP configured for the invitation and notification e-mails. | | Master switch | **Reseller settings › General › `rs_enabled`** is on by default. | ## Prepare the infrastructure 1. Decide the public address of the server: its IPv4 address and, if you prefer CNAMEs, a host name that points at it (for example `sites.cloud.example.com`). 2. Open **Customers › Resellers › Defaults** (`/resellers/settings`), group **Domains & SSL**, and set: | Key | Meaning | |---|---| | `rs_server_ipv4` | The public IPv4 that resellers' A records must point to. | | `rs_cname_target` | The host name resellers' CNAME records must point to. | | `rs_master_hosts` | Host names that belong to your own main site (they cannot be given to resellers). | | `rs_platform_domain` | Optional parent domain for **platform sub-domains** (for example `partners.cloud.example.com` to give resellers `acme.partners.cloud.example.com`). Empty disables the option. | | `rs_sites_stale_minutes` | Minutes before the certificate service is considered stale (default 15). | | `rs_ssl_fail_hours` | Hours of failed attempts before an error is raised (default 48). | ::: warning Check these values before sending instructions `rs_server_ipv4`, `rs_cname_target` and `rs_master_hosts` ship with example values. Replace them with your own server and domain **before** you give DNS instructions to a reseller, otherwise the instructions on screen point to the wrong place. ::: 3. Click **Save** (a reason is requested). 4. Make sure your reverse proxy forwards the reseller host names to the NexCMP panel and serves a certificate for each of them. The installer configures nginx and Let's Encrypt only for the main domain (and `admin.` and `partner.` names that resolve). If you use Cloudflare, proxy the host name and use the Full SSL mode. ## Connect in NexCMP ### Create the reseller 1. Open **Customers › Resellers › List** and click **New reseller**. The wizard has 8 steps and provisions everything at the end; each step can be retried safely. | Step | Fill in | |---|---| | **Reseller** | Legal or trading name, **slug** (4–31 characters a–z, 0–9 or `-`, starting with a letter; it is also the invoice prefix), contact name, e-mail and phone, default language, billing details of the reseller (as a buyer of wallet top-ups). | | **Console account** | Username, account e-mail, full name; **Send a one-time invite link by e-mail** (valid 72 hours, recommended) or show a temporary password once. | | **Brand** | Brand name, short name, taglines, primary colours, support e-mail and phone, the seller name printed on customer invoices, logos (light and dark background), square mark and favicon: PNG, JPEG, WEBP or ICO up to 256 KB, **no SVG**. | | **Domains** | The customer domain(s) with **also www**, which one is primary, a **test domain** (sslip.io) and the **dry-run** option, or a platform sub-domain. The wizard shows **DNS OK / DNS not ready** and the records to create. | | **Pricing & wallet** | Wholesale discount (%), retail floor (% of list), default markup (%), credit limit, initial balance, low-balance alert, limits on customers, VMs and domains, site features (customer sign-up, DDoS protection, support tickets, custom plan, resize, Auto Scaling). | | **Payment channels** | Who collects customer payments (see below) and optional own PayPal and own VN bank transfer. | | **Email** | Sender name and Reply-To. The From address stays the platform address until DKIM is set up for the reseller domain. | | **Review** | Check everything and click **Provision everything**, or **Save draft (provision later)**. | 2. Provisioning continues in the background (every 30 seconds). Open the reseller to see the progress and use **Continue provisioning** or **Retry** if a step failed. ![New reseller wizard on the first step with example values](https://nexcmp.com/docs/images/customers/resellers-01-wizard.webp) ### Point DNS (the reseller does this) The reseller (or you) creates the records shown on the **Domains & SSL** tab: | Record | Name | Value | |---|---|---| | TXT (ownership, first time only) | `_site-verify.` | `sv=` shown on screen | | A (apex domain such as `shop.example.net`) | the domain | your `rs_server_ipv4` | | CNAME (sub-domain such as `cloud.example.net`) | the domain | your `rs_cname_target` (or an A record to `rs_server_ipv4`) | | CNAME (optional) | `www.` | your `rs_cname_target` | Also: - Remove any **AAAA** (IPv6) record unless your server answers on that IPv6 address. - Turn the CDN proxy **off** (grey cloud) while the domain is being verified. - If the domain has **CAA** records, allow `letsencrypt.org`. The token in the TXT record is different for every domain; copy it from the screen. The system re-checks DNS every 5 minutes for the first 48 hours, then hourly, and releases a name whose TXT record is still missing after 72 hours. ### Domain states On **Customers › Resellers › Domains & SSL** (all resellers) and in a reseller's **Domains & SSL** tab: | State | Meaning | |---|---| | `pending_verify` | Waiting for the TXT record. | | `pending_dns` / `verifying` | Waiting for A or CNAME to point to you. | | `issuing` | Certificate being issued. | | `active` | Serving the reseller's site. | | `cert_expiring` | Certificate close to expiry; renewal expected. | | `dns_lost` | DNS stopped pointing to you; after 30 days the domain is removed. | | `error` | See the error code; **Retry** after fixing. | | `removing` / `removed` | Being taken away; the license slot is returned. | Row actions: **Check now**, **Retry**, **Make primary**, **Test mode** (for a test domain), **Remove**. ## Permissions & wholesale **Customers › Resellers › Permissions & wholesale** (`/resellers/entitlements`) is a matrix: one row per reseller, with the wholesale **discount** and **markup** and one switch per service (for example DDoS Shield and VPN). A switch is editable when the platform-wide feature is on; otherwise the cell is read-only and links to the setting. The **Top-up codes** sub-tab sets how many codes each reseller may issue per month (see [Promotions](https://nexcmp.com/docs/billing/promotions/)). Pricing rules: - Wholesale price = list price x (1 - discount), and can be overridden per plan and per billing cycle in the reseller's **Wholesale pricing** tab. - The reseller sets retail prices and cannot go below the **floor** (percentage of list). - The reseller's **wallet** pays the wholesale price when their customers buy. The wallet has a credit limit, a grace period and an overdraft cap (see Defaults). ## Defaults **Customers › Resellers › Defaults** (`/resellers/settings`) are the `rs_*` keys that apply to every reseller. Each change asks for a reason and is audited. | Group | Key | Default | Meaning | |---|---|---|---| | General | `rs_enabled` | 1 | Reseller programme on. | | | `rs_platform_collect_enabled` | 0 | Allows the platform to collect customer payments for resellers. **Enable only after legal review.** | | | `rs_totp_required` | 1 | Reseller console accounts must use two-factor authentication. | | | `rs_agreement_version` | 1 | Version of the reseller agreement they must accept. | | Default pricing | `rs_default_discount_bp` | 2000 (20 %) | Wholesale discount for new resellers (basis points, 0 to 9000). | | | `rs_default_markup_bp` | 2500 (25 %) | Default markup (0 to 50000). | | | `rs_default_floor_bp` | 9000 (90 %) | Retail floor (0 to 20000). | | | `rs_default_max_domains` | 3 | Domains allowed per reseller. | | Wallet and payouts | `rs_grace_hours` | 72 | Grace before services of customers are affected by an empty reseller wallet. | | | `rs_overdraft_cap_cents` | 5000 | Overdraft allowed on the wallet. | | | `rs_retention_days` | 14 | Days before the VMs of a closed reseller are destroyed. | | | `rs_payout_hold_days` | 30 | Days a commission is held before it can be withdrawn. | | | `rs_payout_min_cents` | 5000 | Smallest withdrawal. | | | `rs_topup_min_cents` | 2000 | Smallest wallet top-up by a reseller. | | Payments and mail | `rs_webhook_base` | empty | Public base URL for reseller gateway webhooks (empty = use the site URL). | | | `rs_mail_from` | empty | From address for reseller e-mails. | | | `rs_invoice_show_processor` | 1 | Show the payment processor on invoices. | | | `rs_gw_stripe_enabled`, `rs_gw_usdt_enabled` | 1 | Whether resellers may configure their own Stripe or USDT. | ## Who collects customer payments | Mode | Meaning | |---|---| | **Reseller collects (own channels)** — default | The reseller connects their own Stripe, PayPal, USDT or bank account. Their webhooks are `https:///v1/billing//webhook/r/`. Saved cards and [auto top-up](https://nexcmp.com/docs/billing/auto-top-up/) are not available for these sites. | | **Platform collects** | Your channels are shown on the reseller site. Locked until `rs_platform_collect_enabled = 1`. | | **Mixed** | A combination chosen per channel. | ## What the reseller sees The partner console at `https://partner.` has Overview, Reports, Customers, VMs, Tickets, Orders, Payments, Prices, Brand, Landing & legal, Domains & SSL, Email, Wallet and Payment channels. Console accounts have the role *reseller* (full) or *staff* (read-only). To look at it yourself, open the reseller and click **Sign in to partner console**: you enter for 60 minutes, every write is logged with your name in both the admin log and the reseller log, and money operations need a second confirmation. ## Verify 1. After provisioning, the reseller's list row shows *active* and a green **Provisioning** column. 2. Open the reseller's domain in a browser: the reseller's own login page appears with their brand. `https:///admin` answers 404. 3. Sign up a test customer on that domain: the customer appears under **Customers › List** with the reseller in the **Site** column. 4. The license page shows one more used slot. ## Limits & notes - One license slot per custom or test domain; platform sub-domains do not use a slot. - Suspending a reseller keeps data; **Close reseller** returns all domain slots, and VM destruction is allowed only after the retention period. - Reseller customers appear in your customer list and can be impersonated like any other (the link opens on the reseller's domain). - Resellers cannot see or change your payment channels, quotas defaults or other resellers. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Out of license reseller domain slots" | All slots used | Free a slot (remove a domain) or buy more on the license page. | | Domain stuck in `pending_verify` | TXT record missing | Create the TXT record exactly as shown; use **Check now**. | | Domain stuck in `pending_dns` | A/CNAME not pointing to you, an AAAA record, or the CDN proxy is on | Fix DNS, turn the proxy off, then **Check now**. | | `error` state | See the code on the row (for example CAA blocking Let's Encrypt, rate limit) | Fix and click **Retry**. | | Browser says the host is unknown or "421" | The host name is not on the license, or not routed to the panel | Add the domain through the reseller (consumes a slot) and fix the reverse proxy. | | Instructions show a wrong IP or host | `rs_server_ipv4` / `rs_cname_target` still have the sample values | Set them in Defaults. | | Reseller cannot enable platform collection | Locked | Set `rs_platform_collect_enabled` after legal review. | --- # Branding > **Where in the admin:** System › Settings › Brand (`/settings/branding`). Related: System › Settings › General (`/settings`) for URLs and the seller details printed on invoices. ## What it does NexCMP is white-label: the product carries no vendor name, and everything your customers see is **your** brand. Brand settings apply to the login and sign-up pages, the portal, the admin console and e-mails. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Files | Logos: SVG, PNG, JPEG, WEBP or ICO, **up to 512 KB each**. | | Dependencies | None. | ## Steps 1. Open **System › Settings › Brand**. 2. In the card **Identity** fill in: | Field | Notes | |---|---| | **Brand name** (required) | Up to 60 characters. Shown in the portal, e-mails, invoices and legal pages. | | **Seller name on invoices** | Up to 120 characters. Empty means the brand name. | | **Brand color** | A `#rrggbb` colour; the small button next to the field resets it to the built-in colour. | | **Site currency** | **USD**: prices and balances are shown in USD only, with no currency switch. **VND**: also shows the converted amount at the stored exchange rate (see [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/)). | 3. Click **Save**. 4. In the card **Logo & favicon** upload the three images — **Logo (light background)**, **Logo (dark background)** and **Favicon** — with **Upload** (or **Replace**); **Remove** deletes one. ![Brand page with the identity fields and the three uploaded images](https://nexcmp.com/docs/images/customers/branding-01-brand.webp) ### Related settings in General **System › Settings › General** has two tabs that complete the picture: | Tab | Fields | |---|---| | **Branding & URLs** | Brand name (e-mail, invoices), default language (English or Vietnamese), **Customer app URL**, **Console URL**, **Admin URL** (reseller impersonation link) and **Partner console URL** (invite links, reseller e-mails). The setup wizard fills these with `https://`, `https:///admin` and `https:///console`. | | **Seller & invoicing** | Legal entity name, tax ID, address, contact e-mail and the invoice number prefix. See [Invoices and ledger](https://nexcmp.com/docs/billing/invoices-and-ledger/). | The **From name** of e-mails is set in **System › Settings › Email** ([SMTP and e-mails](https://nexcmp.com/docs/administration/smtp-and-emails/)). ## Uploaded files are checked - Raster images are recognised from their content (not from the file name) and measured. - **SVG files are rebuilt** from a safe list of shapes. A file containing scripts, `foreignObject`, event attributes, external links, `url(...)` pointing outside, a DOCTYPE or entities is **refused** (`svg_unsafe`). - A file that is too large is refused with `asset_too_large`, a wrong type with `asset_type`, bad dimensions with `asset_dimensions`. - Files are served from your own domain with strict security headers. ## Verify 1. Open the portal login page in a private window: the logo, name and colour appear. 2. Check the browser tab for the favicon (browsers cache favicons; reload with the cache cleared if you still see the old one). 3. Trigger a test e-mail (for example **Send reset link** for a test customer) and check the brand name in the subject and footer. ## Limits & notes - The logo for the dark background is used when the customer or admin switches to the dark theme. - Resellers have their own brand; they upload PNG, JPEG, WEBP or ICO up to 256 KB (no SVG). See [Resellers](https://nexcmp.com/docs/customers/resellers/). - A change to the brand colour or logo shows on the next page load; e-mails already sent are not changed. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Upload refused: `svg_unsafe` | The SVG has scripts, links or external references | Export a plain SVG (no script, no external images) or use PNG. | | `asset_too_large` | File over 512 KB | Compress or resize the image. | | `asset_type` | Not SVG, PNG, JPEG, WEBP or ICO | Convert the file. | | `invalid_argument: primary` | The colour is not `#rrggbb` | Use a six-digit hex value. | | Old logo still visible | Browser cache | Hard-reload; favicons can take longer. | | Prices show only in USD | Site currency is USD | Switch to VND and make sure an exchange rate is stored. | --- # Legal pages > **Where in the admin:** System › Settings › Legal (`/settings/legal`). **Where customers see them:** `https:///terms` and `/privacy`, linked from sign-up and checkout. ## What it does Your installation publishes two pages: **Terms of Service** and **Privacy Policy**. Until you write your own, NexCMP shows a **neutral default text** that does not mention the software vendor and promises no particular service. Replace it with text reviewed by your lawyer before you start selling. ## Requirements | Item | Requirement | |---|---| | Role | An administrator. | | Content | Plain markdown, up to **50 KB** per page and language. | | Dependencies | Brand name and seller details ([Branding](https://nexcmp.com/docs/customers/branding/)) are inserted into the default text. | ## Steps 1. Open **System › Settings › Legal**. Each of the two pages has its own card with the tag *Using the default text* or *Using your text*. 2. Choose the language tab, **English** or **Tiếng Việt**. 3. Type your text. While the box is empty the default text is shown faintly as a placeholder. To start from it, click **Start from the default text**, then edit. 4. Click **Save**. The size counter shows `used / 50 KB`. 5. Click **View on the customer site** to open `/terms` or `/privacy`. 6. To go back to the default for one language, click **Clear, back to default** and save. ![Legal settings with the Terms of Service card showing the default text as a starting point](https://nexcmp.com/docs/images/customers/legal-01-terms.webp) ## What the default pages contain The default text is stored in the product as `legal_default.json` and filled with your brand name, seller name, site URL and contact e-mail (from **Seller & invoicing** under System › Settings › General; if no e-mail is set it points to your support team). | Page | Sections | |---|---| | **Terms of Service** | 1 The service · 2 Accounts and verification · 3 Prices and billing · 4 Refunds · 5 Your data · 6 Acceptable use · 7 Availability and support · 8 Suspension and termination · 9 Liability · 10 Changes and governing law (the law of the seller's place of business) | | **Privacy Policy** | 1 What we collect · 2 Why we use it · 3 Cookies and local storage · 4 Who else sees it · 5 Retention · 6 Your rights · 7 Contact | ::: warning Not legal advice The default text is a neutral template. Have it reviewed before using it with real customers, especially the sections on refunds, liability and governing law. ::: ## Markdown rules - Supported: headings (`# 1. Title`), paragraphs, bullet and numbered lists, **bold**, and links `[text](https://example.com)`. - Links must start with `http://` or `https://`. - **Raw HTML is refused** (any ``, ` # Customer portal overview > **Where in the portal:** `https:///` — customers sign in at `/login`. Administrators use `/admin`; the same login opens both when the account is an administrator. The portal is what your customers use every day. It is branded with your name, logo and colours ([Branding](https://nexcmp.com/docs/customers/branding/)) and shows only the services you have turned on ([Modules](https://nexcmp.com/docs/infrastructure/modules/)). ## The dashboard After sign-in the **Dashboard** shows: | Block | Meaning | |---|---| | Wallet balance | Current credit and an estimate of how many days it lasts at the present burn rate | | Spent this month | Charges so far and a forecast for month end | | Virtual machines | Each VM with status, IP, CPU and RAM, and quick actions (Console, Restart) | | Bandwidth | Inbound/outbound traffic for 1 h, 6 h, 24 h, 7 d, 30 d | | Recent activity | Latest events on the account | | Quick start | A checklist: create a VM, add an SSH key, top up, enable auto top-up, verify e-mail | ![The customer dashboard with wallet balance, spend forecast, a running virtual machine and the quick-start checklist](https://nexcmp.com/docs/images/portal/overview-01-dashboard.webp) ## Navigation The menu groups services by type. A service you disabled (or whose source is not connected) does not appear. | Area | Page | |---|---| | Instances | [Virtual machines](https://nexcmp.com/docs/portal/virtual-machines/) and their detail pages | | Deploy | [Ordering](https://nexcmp.com/docs/portal/ordering/) — create a resource | | Storage | Snapshots, backups and volumes when enabled — [Snapshots](https://nexcmp.com/docs/portal/snapshots/) | | Billing | [Wallet and top-ups](https://nexcmp.com/docs/portal/sign-up-and-wallet/), invoices, orders, quotas | | Account | Profile, sign-in and security, preferences, auto top-up | | Support | Chat widget and tickets — [Support](https://nexcmp.com/docs/portal/support/) | | API docs | `/apidoc` — [API](https://nexcmp.com/docs/developers/api/) | ## Account **Account** has four tabs: **Profile** (name, e-mail, customer ID, delete-account request), **Sign-in & security** (password, passkeys, linked Google/GitHub), **Preferences** (language, display currency) and **Auto top-up** ([Auto top-up](https://nexcmp.com/docs/billing/auto-top-up/)). A customer cannot delete the account directly: they press **Request account deletion**; after all virtual machines are deleted, an operator reviews the request (Customers › Deletion requests) and the customer can withdraw it while pending. ## Language and currency The portal is available in 16 languages and shows prices in a display currency chosen by language; see [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). Customers can switch both in **Preferences**. ## Customer quotas **Billing › Quotas** shows how much of each resource (CPU, memory, storage, IP, VM) the customer may use, based on what they have paid — see [Quotas](https://nexcmp.com/docs/billing/quotas/). --- # Sign-up, wallet and top-up > **Where in the portal:** `/register`, `/login`, Billing (`/billing`). ## Create an account 1. The customer opens `https:///register` and enters e-mail and password (at least 10 characters with letters and digits), or uses **Google**, **GitHub** or a **passkey** when you enabled them — see [Security](https://nexcmp.com/docs/administration/security/#customer-sign-in). 2. If captcha is enabled, they complete it. 3. A verification e-mail is sent through your [SMTP](https://nexcmp.com/docs/administration/smtp-and-emails/). Verifying is required before creating a VM when your sales settings require it. ![The registration page with e-mail and password fields](https://nexcmp.com/docs/images/portal/sign-up-and-wallet-01-register.webp) ::: tip Without working SMTP customers cannot verify their e-mail or reset a password. Configure [SMTP](https://nexcmp.com/docs/administration/smtp-and-emails/) first. ::: ## The wallet Everything is paid from a **wallet** balance in your billing currency. The **Billing** page shows: | Section | Meaning | |---|---| | Spending power | Balance plus pending top-ups | | Accruing charges | Hourly services consumed in the current cycle, not yet settled | | Outstanding balance | Any amount owed | | Billing history | Tabs **Invoice History**, **Payment Order**, **Ledger**, **Orders** | ![The Billing page with spending power, accruing charges and the history tabs](https://nexcmp.com/docs/images/portal/sign-up-and-wallet-02-billing.webp) ## Add credit 1. Click **Deposit**. 2. Enter the amount. The minimum is your [minimum top-up](https://nexcmp.com/docs/billing/minimum-top-up/). 3. Choose a payment channel (card, PayPal, USDT, or bank transfer when the interface is Vietnamese) — channels are those you enabled in [Payment channels](https://nexcmp.com/docs/billing/payment-channels/). 4. Pay. The balance updates when the payment provider confirms. Saved cards, Stripe Link and PayPal can be reused and used for [auto top-up](https://nexcmp.com/docs/billing/auto-top-up/); see [Saved payment methods](https://nexcmp.com/docs/billing/saved-payment-methods/). ## Redeem a code **Redeem code** adds credit from a voucher you issued (Finance › Billing › Vouchers) or applies a promotion code — see [Promotions](https://nexcmp.com/docs/billing/promotions/). ## Invoices Each paid order produces an invoice that shows your seller details ([General settings](https://nexcmp.com/docs/administration/general-settings/)). Customers open it from **Invoice History** and print or save it as PDF from the browser. ## Hourly charges Hourly services are charged every hour the resource exists; amounts accumulate and a single hour can round to $0.00. When the balance is not enough, services are suspended according to your billing rules and the customer is told by e-mail (`low_balance`). --- # Ordering a service > **Where in the portal:** Deploy (`/deploy/vm`). **Where in the admin** you control what appears here: Services (plans, prices) and System › Admin › License & updates (modules). The order page is one scrolling form with numbered steps. Picking another service at step 1 keeps the page and changes the steps below. ## Steps 1. **Service** — Cloud Servers, and the other services you enabled (Kubernetes, Object Storage, Images, Private Cloud, VPN …). 2. **Location** — the [zone](https://nexcmp.com/docs/infrastructure/zones/). Prices and included traffic can differ by location. 3. **Plan** — a fixed plan (vCPU, RAM, NVMe disk, hourly price) or **Custom** (the customer picks vCPU, RAM and disk; priced per resource). Plans come from [Plans](https://nexcmp.com/docs/catalog/plans/). 4. **Billing cycle** — **Hourly** or prepaid monthly/yearly cycles with a discount (for example 1 year −10%, 2 years −15%, 3 years −20%). 5. **Operating system** — a list of templates, **My Images** (the customer's own uploads), and the version. 6. **Hostname & quantity** — a name (or **Randomize**) and how many servers. ![The order page showing the service, location and plan steps](https://nexcmp.com/docs/images/portal/ordering-01-plan.webp) ![The billing cycle and operating system steps of the order page](https://nexcmp.com/docs/images/portal/ordering-02-cycle-os.webp) ## Placing the order The summary shows the price. The order is charged to the wallet **before** provisioning, after the [quota check](https://nexcmp.com/docs/billing/quotas/) and [minimum balance](https://nexcmp.com/docs/billing/minimum-top-up/) rules. A VM usually reaches *Running* in about a minute; the customer sees deploy steps in the VM's **Activity** tab and receives the "VM ready" e-mail with the initial password (visible once for 10 minutes). ## What can stop an order | Message / cause | Fix | |---|---| | Not enough balance | Top up — [Sign-up, wallet and top-up](https://nexcmp.com/docs/portal/sign-up-and-wallet/) | | Quota exceeded | Raise the quota — [Quotas](https://nexcmp.com/docs/billing/quotas/) | | E-mail not verified | Customer verifies the e-mail | | No plan for the chosen location | Make a plan available in that zone — [Plans](https://nexcmp.com/docs/catalog/plans/) | | License not valid | Customers cannot create new services — [License](https://nexcmp.com/docs/getting-started/license-and-purchase/#if-the-license-is-not-valid) | --- # Virtual machines > **Where in the portal:** Instances (`/vms`) and a VM's page (`/vms/`). **Instances** lists a customer's VMs with name, OS, status, IP, specs and cost. Click one to open its page. ## The VM page Tabs: **Overview**, **Console**, **AI SSH Agent** (when the AI module is on), **Monitoring**, **Storage**, **Network**, **Configuration**, **Billing**, **Activity**. ![A virtual machine overview with status, specs, IP, bandwidth chart and action log](https://nexcmp.com/docs/images/portal/virtual-machines-01-overview.webp) ### Actions | Action | Effect | |---|---| | Console | Opens the browser console — [Console](https://nexcmp.com/docs/portal/console/) | | Restart / Stop / Force stop | Power control | | Reset password | Generates a new password, shown once | | Reinstall OS | Rebuilds from a template; data is lost | | Change IP | Replaces the public IP (where the source supports it) | | Enable Auto Scaling | Scales CPU/RAM under load when enabled | | Delete | Destroys the VM; hourly billing stops | ### Information shown Uptime, cost this month, vCPU/RAM/disk with live use, public and private IP, user name, bandwidth chart, the action log, and a billing summary (plan, cycle, hours, remaining). ::: info Fields depend on the source A VM page shows only what its source supports. For example, a Proxmox VE VM has no port forwarding or change-IP, and its OS list follows the template set of its cluster. See the capability table in [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). ::: ## Billing tab Shows plan, cycle, total hours, remaining time and the billing log. Hourly VMs accrue until deleted. A customer can **switch cycle** to prepay a longer period at a discount. ## For administrators The same VM is visible under Services › Virtual Machines in the admin console, where you can act on behalf of the customer or [log in as](https://nexcmp.com/docs/customers/login-as/) them. --- # Console > **Where in the portal:** a VM's page → **Console** tab. **Where in the admin:** System › Admin › License & updates → modules (web SSH). ## What it does The **Console** tab opens a graphical (VNC) session to the VM in the browser, even when the network inside the VM is broken. A **web SSH** terminal is available as a second option when the SSH module is enabled. ## Requirements | Item | VNC console | Web SSH | |---|---|---| | CloudStack | Console proxy VM (CPVM) reachable by the panel — see [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) | A reachable SSH port on the VM | | Proxmox VE | Works through the panel (noVNC over the Proxmox API; the TLS fingerprint is pinned) — see [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) | Same | | Panel | Console URL set in [General settings](https://nexcmp.com/docs/administration/general-settings/) | The `ssh-proxy` service and module flag `feature_ssh_web`; off by default — see [Modules](https://nexcmp.com/docs/infrastructure/modules/) | If the web SSH module is off the tab shows **VNC only**. ## Use it 1. Open the VM → **Console**. 2. Click **Console** (or **Open in new window**). A short-lived ticket is issued and the console opens at `https:///console`. 3. Log in with the VM's user name and password (visible on the Overview tab; reset it if lost). ![The Console tab of a virtual machine](https://nexcmp.com/docs/images/portal/console-01-tab.webp) ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Disconnected" immediately | Browser kept an old console path | Clear site data for the domain and retry | | Black screen | VM is stopped or booting | Start the VM and wait | | Console does not open on CloudStack | Console proxy VM not running or not reachable | Check the system VMs in CloudStack | | No SSH option | Web SSH module off | Enable it only after `ssh-proxy` is installed | --- # Snapshots and backups > **Where in the portal:** Snapshots (`/snapshots`), Snapshot schedules (`/snapshots/schedules`), Backups (`/backups`). Visible only when the feature is enabled in the admin. ## What it does A snapshot saves the state of a VM's disk so the customer can roll back after a bad change. Snapshots can be created by hand or on a schedule. ## Availability | Source | Snapshots | Notes | |---|---|---| | CloudStack | Yes | Volume snapshots; storage billed as configured | | Proxmox VE | Yes | Up to **5 per VM**; create, list, restore and delete | The menu entry appears only when the snapshot feature is on and the customer has a VM whose source supports it. ## Use it 1. Open **Snapshots**, click **Create**, pick the VM and give a name. 2. Wait until the status is *ready*. 3. To go back, choose **Restore** on the snapshot and confirm — the VM returns to that state (and is restarted when needed). 4. Delete snapshots you no longer need. ::: warning Restoring discards everything written after the snapshot. Protected VMs cannot be restored or destroyed from the portal. ::: --- # Support for customers > **Where in the portal:** the chat bubble on every page, `/chat`, and Support tickets. **Where in the admin:** Customers › Support › Tickets (`/support/tickets`), tab **Settings**. ## For customers - **Chat bubble** (bottom corner of every page): ask a question; the [AI Chat](https://nexcmp.com/docs/ai/ai-chat/) answers using your documentation and the customer's own account data. **Expand** opens the same conversation on `/chat`. - **Talk to staff**: hands the conversation to your team (via your admin Telegram group). - **Telegram / Zalo** buttons: appear only after you set the links in the support settings. - **Tickets**: for longer problems; replies are e-mailed (`ticket_reply`). ## For you 1. Open **Customers › Support › Tickets**; new tickets and chats appear here with the conversation. 2. Reply in the thread. The customer is e-mailed through your [SMTP](https://nexcmp.com/docs/administration/smtp-and-emails/). 3. In tab **Settings** turn the chat widget on or off, set the Telegram link and Zalo link (https only), and choose whether the AI answers automatically — see [AI Chat](https://nexcmp.com/docs/ai/ai-chat/). 4. Connect [Telegram alerts](https://nexcmp.com/docs/administration/telegram-notifications/) so your team is notified of new chats. --- # AI provider > **Where in the admin:** System › Settings › AI provider (`/settings/ai`). The AI module switch is there and on System › Admin › License & updates (`/license`), card **Module**. ## What it does AI Chat (automatic support chat) and the AI Agent (VM administration in plain language) do not ship with a built-in model. You choose **your own provider**: any service that offers an **OpenAI-compatible** `chat/completions` API — for example OpenAI, OpenRouter, a LiteLLM gateway or your own vLLM server. NexCMP stores the endpoint and the API key (encrypted) and uses them for every AI feature. You pay the provider directly. ## Requirements | Item | Requirement | |---|---| | Endpoint | An OpenAI-compatible base URL (`https://api.openai.com/v1`, `https://ai.example.com/v1`). NexCMP appends `/chat/completions` to a URL that ends in `/v1`; for any other URL it appends `/v1/chat/completions`; a URL that already ends in `/chat/completions` is used as is. | | Model | A model that supports **tool (function) calling**. Both AI Chat and the Agent rely on it. | | API key | A key for that endpoint. Prefer a key with a **spending cap** on the provider side (for example a LiteLLM virtual key with a monthly budget). | | Network | The panel server must reach the endpoint over HTTP(S). A URL typed in the form is only allowed to point to the Internet on ports 80, 443, 8080 or 8443; to use an internal address save the provider first and then test. | | Dependencies | The **AI** module on. For the Agent see [AI Agent](https://nexcmp.com/docs/ai/ai-agent/#requirements). | ## Prepare the infrastructure 1. Sign up with the provider and create an API key. If the provider supports budgets or rate limits, set a monthly limit. 2. Note the **base URL** and the exact **model name** (and, optionally, up to four fallback models). 3. Test it from the NexCMP server before entering it: ```bash curl -s https://ai.example.com/v1/chat/completions \ -H "Authorization: Bearer XXXX" -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Reply with one word: OK"}]}' ``` A JSON answer with `choices[0].message.content` means the endpoint works. ## Connect in NexCMP 1. Open **System › Settings › AI provider**. 2. In the card **Module AI** switch the module **on**. Until it is on, customers do not see AI Chat or the AI Agent in the portal. (You can also switch it in **License & updates**.) 3. In the card **Connection** fill in: | Field | Example | Notes | |---|---|---| | Use this provider | on | Needs a base URL, a model and a key to be switched on. | | Display name | `LiteLLM` | Up to 60 characters. | | Base URL (OpenAI-compatible) | `https://ai.example.com/v1` | `http://` or `https://`, up to 300 characters, no spaces. | | Provider API key | `sk-XXXX` | Stored encrypted; afterwards only the last 4 characters are shown. Leave empty to keep the stored key. | | Default model | `gpt-4o-mini` | Letters, digits and `- _ . / : @`. | | Fallback models | `gemini-2.5-flash` | Comma separated, up to 4. Used when the default model fails. | | Reasoning effort | Default (not sent) | `Minimal`, `Low`, `Medium` or `High`. For reasoning models (Gemini, o-series) a low level answers much faster. Leave on Default for normal models. | 4. Click **Save**, then **Test connection**. The test sends one short question and shows `Connected — , ms` or the error returned by the provider. ![AI provider page with the AI module switched on and the connection form filled in with example values](https://nexcmp.com/docs/images/ai/ai-providers-01-connection.webp) ### What saving does - The provider settings are saved as one configuration entry and the key is encrypted with AES-256-GCM, bound to this purpose, using the installation's master key. - The support settings **Primary model** and **Fallback model** (used by AI Chat) are set to your default and first fallback model automatically. - The change is written to the audit log (endpoint, model, whether the key changed — never the key). ## Verify 1. **Test connection** shows green. 2. Sign in to the portal as a test customer: the menu now offers **AI Chat** (or the chat bubble) and **AI Agent**. 3. Ask AI Chat "What is my balance?". An answer that quotes your real balance shows that the provider handles tool calls correctly. ## Limits & notes - **One provider.** NexCMP uses a single configured provider for Chat, Agent and ticket investigation. Fallback models give resilience within that provider. - **Key safety.** The saved key is sent only to the saved endpoint. If you change the Base URL you must type the key again before testing. - **Limits per customer.** AI Chat is for signed-in customers only, with per-minute and per-chat message limits. The Agent has a daily call limit per account. See [AI Chat](https://nexcmp.com/docs/ai/ai-chat/) and [AI Agent](https://nexcmp.com/docs/ai/ai-agent/). - **Privacy.** Customer questions and the data the AI reads for that customer (read-only) are sent to your provider. Say so in your [privacy policy](https://nexcmp.com/docs/customers/legal/). - **Cost control.** NexCMP does not meter AI cost; use the provider's budget features. - The module can be switched off at any time: the AI menu items disappear and the AI routes answer 404 `module_disabled`. Some background jobs of AI Chat (ticket investigation, delayed replies) pick up the change at the next restart of the panel. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `base_url must be an http(s) URL` | Missing scheme or spaces | Use `https://host/v1`. | | `api_key is required to enable the provider` | No key stored | Enter the key and save. | | `base_url and model are required to enable the provider` | Empty field | Fill both. | | "Base URL blocked: public http(s) only, ports 80/443/8080/8443" | Testing an unsaved internal address or other port | Save the provider first, then test. | | `api_key is required (enter the key again when you change the endpoint)` | The Base URL differs from the saved one | Retype the key. | | `model: letters, digits and - _ . / : @` | Invalid character | Use the provider's exact model id. | | `fallback_models: up to 4 model names` | Too many | Keep four or fewer. | | Test fails with 401 | Wrong key | Check the key and the endpoint. | | Test fails with 404 or "model not found" | Wrong path or model | Check the base URL (`/v1`) and the model id. | | `Too many tests — wait a minute` (429) | More than 10 tests per minute | Wait. | | Portal still shows no AI menu | Module off, or provider not "in use" | Turn the module on and enable the provider. | --- # AI Chat > **Where in the admin:** Customers › Support › Ticket, tab **AI settings** (`/support/tickets?tab=settings`). Provider and module: System › Settings › AI provider (`/settings/ai`). **Where in the portal:** the floating chat button and the page `/chat` (mode **Ask AI**). ## What it does AI Chat answers customers' questions at any hour, looks up *their own* account data (balance, services, payments, recent errors), and can act for them — create a VM, change a plan — after the customer confirms. When the assistant cannot help, the customer clicks **Talk to a human**: the chat goes to your staff on Telegram and, if nobody answers in time, becomes a ticket. The same AI settings also let the system **investigate tickets** for your staff. ## Requirements | Item | Requirement | |---|---| | Module and provider | The **AI** module on and an [AI provider](https://nexcmp.com/docs/ai/ai-providers/) saved and in use. Without a provider the assistant says it is not configured. | | Customers | **Signed-in customers only.** In an installed NexCMP there is no public site, so a visitor who is not signed in gets "Sign in to use AI Chat". | | Staff channel (optional) | A Telegram bot and chat for admin alerts (see [Telegram notifications](https://nexcmp.com/docs/administration/telegram-notifications/)). The same channel receives chat hand-offs and ticket drafts. | | Dependencies | [AI provider](https://nexcmp.com/docs/ai/ai-providers/). | ## Prepare the infrastructure Not required beyond the AI provider. If you want staff hand-off: 1. Create a Telegram bot with @BotFather and note the token. 2. Create a group or use your private chat with the bot, and note the chat ID. 3. Enter both under System › Settings › Admin alerts (see [Telegram notifications](https://nexcmp.com/docs/administration/telegram-notifications/)). The support features reuse that bot and chat. ## Connect in NexCMP 1. Make sure the AI module is on and the provider tests green ([AI provider](https://nexcmp.com/docs/ai/ai-providers/)). 2. Open **Customers › Support › Ticket** and select the tab **AI settings**. The top of the left card shows three tags: *AI provider: configured*, *Telegram: channel present* and *On shift* or *Off shift*. 3. Left card **AI + Telegram**: | Switch or field | Default | Meaning | |---|---|---| | AI investigates tickets automatically | On | For a new ticket or message the AI reads the customer's data (read-only) and writes the likely cause and a draft reply as an **internal note**. It never sends to the customer. | | Telegram alerts to admin | On | The note arrives on Telegram with the buttons Send, Edit and Skip. | | AI auto-reply | Off | Sends simple how-to or sales answers by itself when every safety condition holds (confidence of 85 % or more, no warnings); sent after 3 minutes and cancellable on Telegram. | | Categories allowed to auto-reply | `how_to`, `sales` | Only these. | | Primary model, Fallback model | set from the provider | Normally leave as saved by the AI provider page. | | Telegram user ID allowed to act | empty | Empty means the chat of the alert channel. | 4. Right card **Live chat (customer widget)**: | Field | Default | Meaning | |---|---|---| | Enable chat widget | On | The chat bubble and the `/chat` page. | | Support hours (Vietnam time) | `08:00-22:00` | When staff are expected to answer on Telegram. Outside these hours, or when no Telegram channel exists, **Talk to a human** turns the chat into a ticket at once. | | Max wait for staff (minutes), then convert to ticket | 10 | 1 to 240. | | AI turns / day / customer | 40 | 1 to 1000. After the limit the assistant invites the customer to talk to a human. | | New chats / hour / IP | 20 | 1 to 1000. | | **Zalo OA link** | empty | `https://zalo.me/...` Shows the button **Chat on Zalo** (with a QR code) in the chat. Empty hides it. | | **Telegram link** | empty | `https://t.me/...` Shows a **Telegram** button. Empty hides it. | | System-wide daily cap of AI turns, signed-in customers | 2000 | | | System-wide daily cap, guests | 500 | Own pool; not used while guests cannot chat. | | System-wide daily cap of automatic ticket investigations | 200 | | | Admin path (the "Open admin" Telegram button) | empty | Your admin URL, for example `https://cloud.example.com/admin`. | Each switch or field saves when you change it. 5. At the bottom, **Reply templates** (title, language VI/EN/ZH, text up to 4,000 characters) give staff canned answers. ![AI settings tab with the AI and Telegram switches and the live chat limits and shortcut links](https://nexcmp.com/docs/images/ai/ai-chat-01-settings.webp) ![Customer chat page with the assistant, the Zalo and Telegram shortcut buttons and the option to talk to a human](https://nexcmp.com/docs/images/ai/ai-chat-02-portal.webp) ## What the assistant can and cannot do - **Read:** it searches your knowledge base and your public documentation, and reads the signed-in customer's own data through read-only tools. It cannot see another customer. - **Act:** it uses the customer's own API permissions. A *read* runs at once. Anything that changes or costs money creates a **confirmation card** ("Needs your confirmation", with *Costs money* or *Delete* tags); nothing happens until the customer clicks **Confirm**. A chat can hold at most 3 pending cards and an account at most 10 confirmed write actions per hour. - **Never:** it cannot open consoles, create SSH or console tokens, download Kubernetes access files, reset passwords, or show secrets such as proxy passwords or S3 secrets. Secrets are never put into the model's context or the chat history. - **Safety:** its answers pass an output filter; the customer's text and tool results are treated as data, not as instructions. - **Honesty banner:** the chat shows "Answered by an AI assistant — it may be imperfect. Talk to a human for anything critical." ## Staff hand-off 1. The customer clicks **Talk to a human** and may leave an e-mail. 2. A message with the conversation reaches the staff Telegram chat. Staff answer by replying to that message; the reply appears in the customer's chat as "[brand] Support". 3. If nobody answers within the wait time (default 10 minutes), the chat becomes a ticket under **Customers › Support › Ticket**. The same happens immediately outside the support hours or when Telegram is not configured, and the customer is told a ticket was opened. 4. Admins also see running chats in the **Live chat** tab. ## Verify 1. Sign in as a test customer, open the chat and ask a question about your services. The assistant answers. 2. Ask it to create something that costs money: a confirmation card appears and nothing is created until you click **Confirm**. 3. Click **Talk to a human**. The message reaches your Telegram chat (if configured) or, after the wait, a ticket appears. 4. With Zalo and Telegram links filled in, both shortcut buttons appear in the chat. ## Limits & notes - **Rate limits (per IP):** 20 messages per minute and a handful of hand-offs per hour; **40 messages per conversation**. These are fixed. - **Cost:** every AI turn calls your provider. Lower **AI turns / day / customer** to cap spending. - **Auto-reply is off by default.** Switch it on only after you have watched drafts for a while. - **Languages:** the assistant answers in the customer's language; Vietnamese is detected from the text. - **Resellers:** reseller sites use tickets instead of the widget. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Customers do not see the chat | AI module off, or **Enable chat widget** off | Turn both on. | | "Sign in to use AI Chat" | Visitor is not signed in | Expected in NexCMP: sign in first. | | The assistant says no AI provider is configured | Provider missing, not in use, or key missing | Complete [AI provider](https://nexcmp.com/docs/ai/ai-providers/). | | AI replies "talk to a human" after a few questions | Daily limit per customer reached | Raise **AI turns / day / customer**. | | Telegram tag shows "no channel" | Bot or chat ID not configured | Set up [Telegram notifications](https://nexcmp.com/docs/administration/telegram-notifications/). | | Zalo or Telegram button missing | The link field is empty | Fill in the link. | | "Too many requests" in the chat | More than 20 messages a minute from one IP | Wait a minute. | | Chat conversation full | 40 messages reached | Start a new conversation. | --- # AI Agent > **Where in the admin:** System › Settings › AI provider (`/settings/ai`) — module switch and provider; System › Admin › License & updates (`/license`) — module switch. **Where in the portal:** AI Agent (`/ai/agent`) and the page `/chat/agent`, or mode **Agent** inside the chat. ## What it does The AI Agent connects to a customer's own Linux VM over SSH and carries out tasks described in plain language: "check system health", "install Docker", "find recent errors", "free up disk space". It shows every command it proposes and its output. Read-only commands can run automatically; anything that changes the server waits for the customer to press **Run**. ## Requirements | Item | Requirement | |---|---| | AI module and provider | The **AI** module on and a working [AI provider](https://nexcmp.com/docs/ai/ai-providers/). The Agent also needs a model that supports tool calling. | | `openssh-client` on the panel server | The Agent runs commands with the panel's own `ssh` program (`/usr/bin/ssh`). The installer installs `openssh-client`; check with `ssh -V`. Without it the Agent reports "ssh client is not installed on the panel server". | | No separate SSH gateway | In the default installation the built-in SSH is used and the `ssh-proxy` web terminal is **not** needed. Only when the optional *web SSH* feature (`feature_ssh_web`) has been enabled and the `ssh-proxy` service installed does the Agent go through `ssh-proxy` instead. | | Network | The panel server must reach the VM's IP on its SSH port (22 by default). Check firewalls and security groups between the panel and the guest network. | | VM | Customer-owned, **running**, not locked, **Linux** (Windows VMs are refused), with a public or reachable IP address. | | SSH credentials | The VM's login user and password stored by NexCMP (saved when the VM was created or its password was reset, encrypted), or the panel's operations key if one is installed. If neither exists the Agent says it cannot log in. | ## Prepare the infrastructure 1. Check the SSH client on the panel server: ```bash ssh -V sudo apt-get install -y openssh-client # only if missing ``` 2. Check the panel server can open SSH to a guest VM: ```bash nc -vz 198.51.100.10 22 ``` Replace the address with a test VM's IP. If this fails, fix the routing or firewall first. 3. Make sure the guest allows password login for the user NexCMP stored (some cloud images disable it), or that the panel's operations key is installed on the guest. ## Connect in NexCMP 1. Complete [AI provider](https://nexcmp.com/docs/ai/ai-providers/): module on, provider saved, **Test connection** green. 2. Nothing else is configured on the admin side. The module switch makes the **AI Agent** entries appear in the customer portal. 3. As a customer: open **AI Agent**, choose a running server and click **Choose server**. The page first probes SSH with a fixed harmless command (no AI call is used) and shows *SSH connected* or the reason it failed. 4. Type a task or pick a quick start (*Check system health*, *Install Docker*, *Install Nginx*, *Find recent errors*, *Security check*, *Free up disk space*). ## How it runs commands - **Where from:** the panel server, with the system `ssh` client, one non-interactive command at a time. The target is always the VM's IP **from the database**, never from the request; only the owner's own running, unlocked VM can be reached. - **Authentication:** the stored password is passed through a one-shot `SSH_ASKPASS` helper in the process environment — it is not written to disk or put on the command line. Host keys are not pinned (VMs are reinstalled with new keys). Output per command is limited to 200 KB and each command has a time limit (60 seconds). - **Agent loop:** the AI proposes commands and reads the outputs; one turn ends after at most **10 steps**. - **Modes:** | Mode | Behaviour | |---|---| | **Confirm** | Every command waits for **Run**. | | **Auto** | Only commands that pass a strict read-only allow-list run by themselves (for example `df -h`, `ps`, `journalctl`). Anything else, and anything the parser cannot analyse with certainty (variables, command substitution, redirects to files), waits for **Run**. | - **Risk labels:** commands that overwrite or delete files, write to disks, change accounts or SSH keys, send data to an outside server, open raw network connections, run obfuscated code, download and run a script, shut down or restart the machine, or reach other servers always show **Needs confirmation**, even in Auto mode. - **Untrusted output:** command output is shown to the model as untrusted data; instructions inside it are not followed. ## Verify 1. Choose a running test VM. The page shows *SSH connected*. 2. Ask "Check CPU, RAM and disk". In Auto mode the read-only commands run and the answer summarises them. 3. Ask "Install Nginx". The install command appears with **Run** and **Skip**; nothing runs until you press **Run**. 4. The counter at the top shows the number of AI calls used today (for example `3/300`). ## Limits & notes - **Daily limit:** 300 AI calls per customer per day (UTC); it resets at 00:00 UTC. When it is used up the Agent answers "Daily AI quota reached". - **Concurrency:** at most 2 Agent runs per customer at the same time; a session runs one turn at a time. - **Sessions:** up to 100 messages per session, 4,000 characters per message. A session belongs to one VM; if the VM is deleted or transferred, the old session becomes read-only. - **Locked or stopped VM, or Windows:** the VM is shown as *Locked*, *Stopped*, *Windows* or *No address* and cannot be chosen. - **Impersonation:** an administrator impersonating a customer cannot use the Agent's credential features (see [Log in as a customer](https://nexcmp.com/docs/customers/login-as/)). - **Responsibility:** the Agent can make mistakes. The portal tells customers to review commands before running them. Tell customers in your terms that they are responsible for commands they approve. - Switching the AI module off removes the Agent entries and the routes answer 404 `module_disabled`. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "ssh client is not installed on the panel server (apt install openssh-client)" | No `ssh` binary | `sudo apt-get install -y openssh-client`. | | "The Agent could not log in" / `ssh: no saved credentials for this VM` | No stored password and no operations key | Reset the VM password in the portal so it is saved again, or install the operations key. | | "The server did not answer in time" | Firewall, SSH not listening, VM busy | Test `nc -vz 22` from the panel server; start `sshd` in the VM. | | "Could not reach SSH on this server" | Routing or security group | Allow the panel server to reach the VM on port 22. | | "VM not running" / "Forbidden" | VM stopped, locked, deleted or not the customer's | Start it, unlock it or pick another. | | "Not available: N (stopped, locked or Windows)" | Reasons listed above | The VM must be Linux and running. | | "The SSH gateway is temporarily unavailable" | Web SSH feature enabled but `ssh-proxy` is down | Start `ssh-proxy`, or turn the web SSH feature off to use the built-in SSH. | | "The AI is responding too slowly (over 90 seconds)" | Provider slow | Use a faster model or a lower **Reasoning effort**. | | "Daily AI quota reached" | 300 calls used | Wait until 00:00 UTC. | | "This session is still running" | Another turn in progress | Wait or press **Stop**. | --- # REST API > **Where in the portal:** API reference at `https:///apidoc` (public) and API keys at `/api-keys`. **Where in the admin:** Customers › API keys (`/customers/apikeys`) lists the keys customers created. ## What it does Every action a customer can do in the portal — list and control VMs, read invoices, manage SSH keys, Kubernetes, storage, support tickets — is available through a versioned REST API under `https:///v1/`. Customers call it from scripts and CI with a scoped **API key**. The same API feeds the [`cmp` command-line tool](https://nexcmp.com/docs/developers/cmp-cli/) and the [MCP server](https://nexcmp.com/docs/developers/mcp/), and the reference is generated from the running installation, so it always lists exactly the features you have switched on. ## Requirements | Item | Requirement | |---|---| | Public HTTPS address | The API is served on your main domain (and on each reseller's own domain). | | An API key | Created by the customer in the portal; see [API keys](https://nexcmp.com/docs/developers/api-keys/). | | Dependencies | None. Endpoints of a module you did not enable (for example Kubernetes or Object Storage) are hidden from the reference and refused. | ## Prepare the infrastructure Nothing to install. If a reverse proxy sits in front of NexCMP, forward `/v1/`, `/mcp`, `/cli/` and `/apidoc` to the panel (the installer's nginx configuration already does). ## Connect in NexCMP ### The interactive reference Open `https://cloud.example.com/apidoc` (replace the host with your domain; a reseller's customers see the reference on the reseller's domain, with the reseller's name). It has endpoint documentation with request and response examples in curl, the `cmp` CLI, JavaScript, Python and Go, a search box (Ctrl or Cmd + K), and a **Try it** panel. Try it sends requests to the same site only, with a key the visitor pastes (kept in the browser session); it is read-only by default, and write or paid calls need an extra switch and a confirmation. ### Downloads and machine-readable documents All are public, need no key and are generated per site: | URL | Content | |---|---| | `/apidoc` | Interactive reference. | | `/v1/public/openapi.json` | OpenAPI 3.1 document. | | `/v1/public/openapi.yaml` | Same, YAML. | | `/v1/public/postman.json` | Postman collection. | | `/v1/public/insomnia.json` | Insomnia export. | | `/v1/public/api.md` | The reference as one Markdown file. | | `/v1/public/api-catalog` | Light JSON: brand, base URL, key prefix, every scope and every endpoint with method, path, scope and whether it needs an `Idempotency-Key`. | ```bash curl -s https://cloud.example.com/v1/public/api-catalog | python3 -m json.tool | head -40 curl -sO https://cloud.example.com/v1/public/openapi.yaml ``` In Postman choose **Import › Link** and paste `https://cloud.example.com/v1/public/postman.json`. The reference page also offers one-file API clients for JavaScript and Python. ### Authentication Send the key in the `Authorization` header: ```bash curl -s https://cloud.example.com/v1/billing/wallet \ -H "Authorization: Bearer vk_XXXX" ``` - Customer keys begin with `vk_`. Keys are **site-bound**: a key created on `cloud.example.com` does not work on a reseller domain. - Browser session tokens (`pt_...`) are for the portal only; use a key for automation. - The key's **scopes** decide what it may call; a missing scope answers HTTP 403 `scope_required`. See [API keys](https://nexcmp.com/docs/developers/api-keys/). - Check a key with `GET /v1/apikeys/self`: it returns the calling key's name, scopes, expiry and the remaining rate-limit allowance (every key may call it). ### Examples ```bash # List virtual machines curl -s "https://cloud.example.com/v1/vm?page=1" -H "Authorization: Bearer vk_XXXX" # Start one (needs vm:write) curl -s -X POST https://cloud.example.com/v1/vm/3f2a1c9e-0000-4000-8000-000000000000/start \ -H "Authorization: Bearer vk_XXXX" # Create a VM (paid: needs vm:create and an Idempotency-Key) curl -s -X POST https://cloud.example.com/v1/vm \ -H "Authorization: Bearer vk_XXXX" \ -H "Idempotency-Key: $(uuidgen)" \ -H "Content-Type: application/json" \ -d @vm.json ``` Take the exact request fields of each operation from `/apidoc`. ### Errors Every error is JSON with a stable `code` and a human-readable `message`: ```json {"error": {"code": "scope_required", "message": "Key lacks scope vm:write"}} ``` | HTTP | Typical `code` | Meaning | |---|---|---| | 401 | `unauthenticated`, `key_invalid`, `key_revoked`, `key_expired`, `key_ip_denied`, `site_mismatch`, `api_keys_disabled` | Missing or unknown key, revoked, expired, caller IP outside the key's allow list, a key from another site, or API keys switched off. | | 403 | `scope_required`, `endpoint_not_public`, `account_blocked`, `quota_exceeded`, `impersonate_forbidden` | Key lacks the scope; the endpoint is not available to keys; the account is locked; a [quota](https://nexcmp.com/docs/billing/quotas/) was reached; action not allowed in an impersonated session. | | 404 | `not_found` | Not found — also returned for resources that belong to someone else. | | 422 | `invalid_argument` | Validation error (field named in the response). | | 429 | `rate_limited` | Too many requests; wait for `Retry-After`. | | 503 | `maintenance` | Maintenance mode is on: reads work, writes are refused. | ### Rate limits | Limit | Default | |---|---| | Per key | 120 requests per minute | | Per account (all keys together) | 300 requests per minute | Every response carries `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset`; a 429 adds `Retry-After`. ### Idempotency Commands that cost money or cannot be repeated (create, change plan, buy, delete ...) **require** an `Idempotency-Key` header (any string up to 200 characters; a UUID works well). Send the same key again after a timeout and the server returns the **original** result (with `Idempotency-Replayed: 1`) instead of repeating the operation. Reusing a key for a different command answers 422; a request still in progress answers 409/`Retry-After`. Without the header a paid call is refused. ## Verify 1. Create a key with scope `vm:read` in the portal (**API keys › Create key**). 2. Run: ```bash curl -i https://cloud.example.com/v1/apikeys/self -H "Authorization: Bearer vk_XXXX" ``` You get HTTP 200 and the rate-limit headers. 3. `curl -s https://cloud.example.com/v1/vm -H "Authorization: Bearer vk_XXXX"` lists the account's VMs. In the portal the key's **Last used** column and the **Key usage log** show the calls. ## Limits & notes - **Never available to a key:** top-up, withdraw, change cards or auto top-up, change the password, e-mail or 2FA, delete the account, create, edit or revoke other keys. Those need a signed-in browser session. - **Hidden modules:** the reference and the OpenAPI document only list features enabled on that site. - **Response size and paging:** list endpoints are paged (`page`, `per_page`); read the OpenAPI document for each endpoint. - **Time and money:** money is in USD cents unless the field name says otherwise. - **Admin API:** the admin console has its own **System › Admin › API** page and administrator keys (`ofw_...`) for administration; they are separate from the customer API described here. - **Versioning:** the path prefix `/v1` is stable; new fields may be added to responses. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | 401 on a new key | Key from another domain, expired, or typed with spaces | Create the key on the domain you call; check the `Authorization: Bearer vk_...` header. | | 403 `scope_required` | Missing scope | Edit the key in the portal and add the scope. | | 401 `key_ip_denied` or `site_mismatch` | Key restricted to other IPs ("allowed IPs") or created on another domain | Add your server's IP to the key's allowed IPs, or call the domain where the key was created. | | 404 on an existing VM | The VM belongs to another account | Use a key of the owner. | | 400/422 asking for `Idempotency-Key` | Paid operation without the header | Add `Idempotency-Key: `. | | 429 | Over the limit | Back off; use `Retry-After`. | | `/apidoc` shows less than expected | Module switched off | Enable the module in License & updates. | --- # API keys and scopes > **Where in the portal:** API keys (`/api-keys`). **Where in the admin:** Customers › Customers › API keys (`/customers/apikeys`). ## What it does An **API key** lets a script, CI job, the [`cmp` CLI](https://nexcmp.com/docs/developers/cmp-cli/) or an AI assistant ([MCP](https://nexcmp.com/docs/developers/mcp/)) call the [REST API](https://nexcmp.com/docs/developers/api/) on behalf of one customer, with only the permissions (**scopes**) that were granted. Customer keys start with `vk_`. They can read and manage services but can **never** touch money or the account itself. ## Requirements | Item | Requirement | |---|---| | Customer | A signed-in portal session (a key cannot create or edit other keys, and an administrator impersonating a customer cannot either). | | Switch | API keys are on by default. If they are switched off, calls answer 401 `api_keys_disabled`. | | Dependencies | The scopes of a product appear only when that product is enabled on the site. | ## Create a key (customer) 1. Sign in to the portal and open **API keys**. 2. Click **Create key**. 3. Fill in the form: | Field | Rule | |---|---| | **Key name** | Required, up to 60 characters ("deploy server", "backup script"). | | **What this key may do** | Tick the scopes you need — at least one. A read-only key should get no write scope. Be careful with scopes that cost money or delete data. | | **Expiry** | Never, 30 days, 90 days or 1 year (the API accepts 1 to 3650 days). | | **Only allow calls from these IPs** | One IP or CIDR range per line, up to 20 (IPv4 or IPv6). Empty means any IP. Pin your server's IP whenever you can. | 4. Confirm the form. The key is shown **once** in a window "Key created — shown ONCE". Copy it and click **I saved the key**. Only a SHA-256 hash is stored, so even an administrator cannot read it again; if you lose it, revoke it and create a new one. 5. The portal also shows a ready `curl` command to test the key. A key looks like `vk_` followed by a long random string. The list shows only the first characters (for example `vk_a1b2c3d4`), the scopes, expiry, last use, request count and status (*Active*, *Expired*, *Revoked*). ### Edit, revoke, audit - **Edit** changes the name, scopes, allowed IPs and expiry. It is not allowed during an impersonated session. - **Revoke** stops the key at once and cannot be undone. The row is kept so its usage log stays available. - **Key usage log** shows write commands and every error of the last 60 days: time, request, key, result and source IP. - An account can hold at most **10 active keys** by default. - Keys created through the CLI or MCP login appear under **Connected apps & devices** and can be revoked there. ## Scopes Scope names have the form `area:action`. Which ones are offered depends on the products you have enabled. | Scope | Allows | |---|---| | `catalog:read` | Read plans and the catalog. | | `vm:read` | Read VMs, metrics and VM invoices. | | `vm:write` | Power actions, minor changes, firewall, quick snapshots. | | `vm:create` | **Create VMs (costs money).** | | `vm:plan` | **Change plan or resize (costs money).** | | `vm:destroy` | **Delete VMs.** | | `vm:restore` | **Restore a VM from a snapshot (overwrites the disk).** | | `vm:console` | Open VNC console tickets. | | `sshkey:read`, `sshkey:write` | Read, add and remove SSH keys. | | `volume:read`, `volume:write`, `volume:create`, `volume:destroy` | Data volumes (create and enlarge cost money; destroy loses data). | | `backup:read`, `backup:write`, `backup:restore` | Backups (restore overwrites the disk). | | `billing:read` | Wallet, invoices, payment history — **read only**. | | `bandwidth:buy` | **Buy a bandwidth add-on (costs money).** | | `account:read`, `account:write` | Profile, overview, activity, notifications; `account:write` only marks notifications as read. | | `apikey:read` | List the account's keys and the usage log (cannot create, edit or revoke). | | `apikey:self` | Implicit on every key: inspect the calling key (`GET /v1/apikeys/self`). | | `ticket:read`, `ticket:write` | Support tickets. | | `aff:read`, `transfer:read` | Affiliate programme and service transfers — read only. | | `k8s:read`, `k8s:write`, `k8s:create`, `k8s:scale`, `k8s:destroy`, `k8s:kubeconfig` | Kubernetes (create and scale cost money; kubeconfig is cluster-admin). | | `vpc:read`, `vpc:write`, `vpc:create`, `vpc:destroy` | Private Cloud. | | `image:read`, `image:write` | OS and ISO images (storage is billed hourly). | | `s3:read`, `s3:write`, `s3:create`, `s3:destroy`, `s3:keys` | Object Storage (the S3 secret is shown once). | | `vpn:read`, `vpn:detail`, `vpn:write`, `vpn:create`, `vpn:destroy` | VPN and IPv6 proxy (connection details include proxy passwords). | | `dedicated:read`, `dedicated:write`, `dedicated:console`, `dedicated:create`, `dedicated:destroy` | Dedicated servers (create orders from the wallet; destroy reinstalls the OS). | | `shield:read`, `shield:write`, `shield:agent` | DDoS Shield, only when that module is enabled. | A call outside the key's scopes answers **403 `scope_required`**; a path that no key may call answers **403 `endpoint_not_public`**. ### What a key can never do No scope opens: topping up or withdrawing money, changing cards or auto top-up, changing the password, e-mail or 2FA, deleting the account, creating, editing or revoking other keys. These always need a signed-in browser. Even a leaked key cannot touch the customer's money or account. ## Rules you can rely on | Rule | Detail | |---|---| | Site-bound | A key works only on the site (domain) where it was created. | | Rate limits | Per key and per account: 120 and 300 requests per minute by default. | | Idempotency | Operations that cost money or cannot be repeated need an `Idempotency-Key` header. | | Quotas | A key is subject to the same [quotas](https://nexcmp.com/docs/billing/quotas/) as the customer. | | Locked account | A locked customer's keys stop working (403 `account_blocked`). | | Audit | Revocation by an administrator is written to the admin audit log. | ## Administrator view 1. Open **Customers › API keys** (`/customers/apikeys`). The table shows key name and first characters, owner, scopes, status, last use (time, request count, IP), expiry. 2. Filter by site if you have resellers. Search by name, ID or key start. 3. Click **Revoke** to stop an active key, or **Log** for its usage. 4. Administrators **see and revoke** keys but cannot read or create them for customers. ![Administrator list of customer API keys with scopes, status, last use and the Revoke button](https://nexcmp.com/docs/images/developers/api-keys-02-admin.webp) ## Verify 1. Create a key with scope `vm:read` only. 2. `curl -s https://cloud.example.com/v1/vm -H "Authorization: Bearer vk_XXXX"` returns the VM list. 3. `curl -s -X POST .../v1/vm//start -H "Authorization: Bearer vk_XXXX"` answers 403 `scope_required`. 4. Revoke the key; the first call now answers 401 `key_revoked`. ## Limits & notes - The key is never shown again. Treat it like a password and store it in a secret manager or CI secret. - Prefer one key per script, minimal scopes, a short expiry and an IP allow list; rotate by creating a new key and revoking the old one. - Keys from device login (CLI, MCP) default to 90 days. - Deleted or transferred resources are unaffected by revoking a key. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Error `too_many_keys` (422) | The limit of active keys (10 by default) is reached | Revoke an old key. | | Error `invalid_argument`: no scope chosen | No scope ticked | Tick at least one scope. | | Error `invalid_argument` on allowed IPs | An entry is not an IP or range, wrong prefix length, or more than 20 entries | One IP or CIDR per line, for example `203.0.113.0/24`. | | Create is refused during impersonation | `impersonate_forbidden` | Ask the customer to create the key. | | 401 `key_expired` | Expiry date passed | Create a new key. | | 401 `key_ip_denied` | Caller IP not in the allow list | Add the IP or clear the list. | | 401 `api_keys_disabled` | Feature switched off | Ask your administrator (`api_keys_enabled` setting). | --- # `cmp` command-line tool > **Where in the portal:** the install commands are on the API reference (`https:///apidoc`, section Command-line interface). Approving a login: `https:///link`. Connected devices: API keys (`/api-keys`) › Connected apps & devices. ## What it does `cmp` is a small command-line program for your customers (and for you) that turns **every API operation into a command**: `cmp vm list`, `cmp vm start `, `cmp billing invoice list`. It is built from the OpenAPI document of **your** installation at release time, so the commands match the features you have enabled. It is one static binary for Linux, macOS and Windows (amd64 and arm64) with nothing else to install. ## Requirements | Item | Requirement | |---|---| | Server | Nothing extra. The release packages are inside the NexCMP release and served by the panel at `https:///cli/`. | | Customer machine | Linux, macOS or Windows (PowerShell 5.1+). `ssh-keygen` (OpenSSH 8.0+) is needed only to verify the signature. | | Credentials | A browser login with a one-time code, or an [API key](https://nexcmp.com/docs/developers/api-keys/). | | Network | HTTPS to your panel. The CLI refuses to send a key over plain `http://` except to `localhost`. | ## Prepare the infrastructure Nothing. The panel serves these files from `/cli/` (no build or upload needed): | File | Purpose | |---|---| | `install.sh`, `install.ps1` | Installers for Linux/macOS and Windows; they contain your panel's address. | | `latest.json` | Current version, file list, sizes, sha256. | | `v/cmp___.tar.gz` or `.zip` | The programs: linux amd64/arm64, darwin amd64/arm64/universal, windows amd64/arm64. | | `v/SHA256SUMS`, `SHA256SUMS.sig` | Checksums and their ed25519 signature. | | `cmp-cli.pub` | The release public key. | ## Install (customer) macOS and Linux: ```bash curl -fsSL https://cloud.example.com/cli/install.sh | sh ``` Windows PowerShell: ```powershell irm https://cloud.example.com/cli/install.ps1 | iex ``` The installer picks the package for the OS and CPU, downloads `SHA256SUMS` and its signature, verifies the signature with the key embedded in the script, verifies the package hash, and installs `cmp` into `/usr/local/bin` (as root) or `~/.local/bin`; on Windows into `%LOCALAPPDATA%\cmp\bin` (added to the user `PATH`). If a check fails nothing is installed. | Variable | Meaning | |---|---| | `CMP_VERSION` | Install this version instead of the latest. | | `CMP_INSTALL_DIR` | Installation folder. | | `CMP_REQUIRE_SIGNATURE` | `1` = abort when the signature cannot be verified. | | `CMP_BASE_URL` | Download base (for mirrors). | Manual download and verification: ```bash curl -fsSLO https://cloud.example.com/cli/latest.json sha256sum --check --ignore-missing SHA256SUMS ``` ![API reference section with the cmp install command for macOS and Linux](https://nexcmp.com/docs/images/developers/cmp-cli-01-install.webp) ## Log in ```bash cmp login ``` 1. The CLI prints a one-time **code** and a link (`https://cloud.example.com/link?code=WDJB-MJHT`), opens the browser and waits. 2. In the browser sign in to the portal, check the client name, device, IP and country, tick the permissions (scopes) and choose how long the key lives (default 90 days). Permissions that cost money or delete data start unticked. Click approve. 3. The CLI receives **its own key**, separate from keys the customer created by hand, and stores it in `~/.config/cmp/config.json` (mode `0600`; `%APPDATA%\cmp` on Windows). It appears under **API keys › Connected apps & devices** and can be revoked there. The code expires after 10 minutes. The approval needs a real signed-in portal session with a verified e-mail — not another key and not an impersonated session. Useful flags: `--scopes vm:read,billing:read`, `--name "build server"`, `--no-browser`, `--profile`, `--api-url`. For CI or servers without a browser, use a key from the portal: ```bash printf %s "$KEY" | cmp login --with-token export CMP_TOKEN=vk_XXXX # nothing is written to disk cmp whoami ``` ## Using commands ```text cmp [subgroup] [ ...] [flags] ``` - Path parameters are positional arguments; query parameters and body fields become kebab-case flags (`--start-port 443`). - `--data '{"a":1}'`, `--data @file.json` or `--data -` sends a whole JSON body; flags override its fields. - `cmp help vm firewall add` (or `--help`) shows the details of any command. ```bash cmp catalog plans cmp vm list --state active --per-page 50 cmp vm get 3f2a1c9e-0000-4000-8000-000000000000 cmp vm start 3f2a1c9e-0000-4000-8000-000000000000 cmp vm firewall add 3f2a1c9e-... --protocol tcp --start-port 22 --cidr-list 203.0.113.0/24 cmp billing wallet cmp api GET /v1/vm --query page=2 # raw call to any endpoint ``` Built-in commands: `login`, `logout`, `whoami`, `config`, `api`, `completion`, `version`, `update`, `help`. Everything else comes from the API document. Global flags: | Flag | Meaning | |---|---| | `-o, --output json\|table\|yaml` | Format. Default `table` on a terminal and `json` when piped. | | `--profile`, `--api-url` | Which stored login and which site. | | `-y, --yes` | Skip confirmation for destructive or paid commands. | | `--idempotency-key ` | Choose the key of a paid command. | | `--retry ` | On HTTP 429 wait for `Retry-After` and retry up to n times. | | `--timeout ` | Seconds to wait for response headers (default 60). | | `--debug` | Show the HTTP exchange on stderr, with keys masked. | Commands that **charge the wallet or delete data** ask for confirmation and, if paid, send an automatically generated `Idempotency-Key` (printed on stderr, so you can retry safely with `--idempotency-key`). Without a terminal they refuse unless `--yes` is given. ### Exit codes | Code | Meaning | |---|---| | 0 | Success | | 1 | Other error (network, HTTP 4xx/5xx not listed) | | 2 | Usage error | | 3 | Not authorized (401/403) | | 4 | Not found (404) | | 5 | Rate limited (429) | | 6 | Confirmation required or declined | | 130 | Interrupted | ### Profiles and environment `cmp config` lists profiles; `cmp config use ` switches; a reseller's customers use their own domain: `cmp login --api-url https://shop.example.net --profile shop`. Precedence: flag, environment, config file. Variables: `CMP_TOKEN`, `CMP_PROFILE`, `CMP_API_URL`, `CMP_OUTPUT`, `CMP_CONFIG_DIR`, `CMP_ALLOW_HTTP`, `CMP_UPDATE_URL`. GitHub Actions: ```yaml - name: Install cmp run: | curl -fsSL https://cloud.example.com/cli/install.sh | CMP_INSTALL_DIR="$RUNNER_TEMP/bin" sh echo "$RUNNER_TEMP/bin" >> "$GITHUB_PATH" - name: List running VMs env: CMP_TOKEN: ${{ secrets.CMP_TOKEN }} # a read-only key run: cmp vm list -o json | jq -r '.items[] | select(.cs_state=="Running") | .hostname' ``` Shell completion: `cmp completion bash|zsh|fish|powershell`. ## Update and uninstall ```bash cmp version cmp update --check cmp update ``` `cmp update` downloads `latest.json`, `SHA256SUMS` and the signature, verifies the signature with the public key **built into the running binary**, verifies the package hash and only then replaces itself. A downgrade needs `--force`. When you update NexCMP, the panel serves the CLI release that ships with the new version. To uninstall delete the `cmp` binary and the config directory (`cmp config path` shows it). Revoke the key under **API keys** if the machine is lost. ## Verify 1. `cmp version` prints the version and the API document version. 2. `cmp login`, approve in the browser, then `cmp whoami` shows the profile and your panel's URL. 3. `cmp vm list` returns your VMs; `cmp vm list -o json | jq .` shows the raw JSON. ## Limits & notes - The command list depends on your build: the packages are generated from your installation's OpenAPI document when the release is built, so a product that is disabled in your release has no command. Use `cmp api` for a path the CLI does not know. - The CLI is under the MIT license. - Each package is signed with an ed25519 key; the signature key is pinned inside the installer and the binary, not fetched at update time. - Keys are limited per key and per account (see [REST API](https://nexcmp.com/docs/developers/api/#rate-limits)). ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `curl .../cli/install.sh` returns 404 | The release on the server has no CLI folder, or the host is not on the license | Check `https:///cli/latest.json` and the domain; update NexCMP. | | "signature could not be verified" | `ssh-keygen` missing or too old | Install OpenSSH 8.0+, or set `CMP_REQUIRE_SIGNATURE=0` only if you accept the risk. | | `cmp login` says the code expired | 10 minutes passed | Run `cmp login` again. | | Exit code 3 | Key lacks a scope, expired or revoked | `cmp whoami`; create or re-approve with more scopes. | | Exit code 6 in a script | Paid or destructive command without a terminal | Add `--yes` (and your own `--idempotency-key`). | | "refuses to send a key over http" | Plain http API URL | Use `https://`, or `CMP_ALLOW_HTTP=1` only for a lab. | | Browser opens the wrong site | `--api-url` not set for a reseller | `cmp login --api-url https://`. | --- # MCP server > **Where in the portal:** API reference › Tools (`https:///apidoc`) shows the endpoint and client snippets; keys are created at API keys (`/api-keys`). There is no setting in the admin: the endpoint is part of the panel. ## What it does The **Model Context Protocol (MCP)** lets an AI assistant call tools. NexCMP serves an MCP endpoint at `https:///mcp`. Every public API operation becomes one **tool** (`vm_list`, `vm_start`, `billing_wallet`, ...), so a customer can ask their assistant "which of my VMs are running?" or "stop the staging VM". The assistant acts with the customer's own API key and permissions only. ::: tip Configuring NexCMP itself with AI? This page is the **customer** MCP endpoint (`/mcp`). To let an AI assistant configure your own panel as an administrator, see [Configure with AI](https://nexcmp.com/docs/configure-with-ai/mcp-admin/) (endpoint `/mcp/admin`, admin keys `ak_`). ::: ## Requirements | Item | Requirement | |---|---| | Server | Nothing to install: the endpoint is served by the panel and reachable wherever the panel is. Your reverse proxy must forward `/mcp` to the panel. | | Customer | An [API key](https://nexcmp.com/docs/developers/api-keys/) (`vk_...`) with the scopes the assistant needs. | | Client | An MCP client that supports the **Streamable HTTP** transport and a custom `Authorization` header: Claude Code, Claude Desktop (through the `mcp-remote` bridge), Cursor, VS Code with Copilot agent mode and similar. | | Protocol versions | 2025-11-25, 2025-06-18 and 2025-03-26. | ## Prepare the infrastructure Nothing, beyond making sure `https:///mcp` is not blocked by a firewall or a proxy rule. A browser-based MCP client from another origin is refused unless that origin is allowed; command-line and desktop clients send no `Origin` header and work as they are. ## Connect (customer side) 1. Create an API key in the portal (**API keys › Create key**) with only the scopes you want the assistant to use, a short expiry and, if possible, an IP restriction. 2. Add the server to your client. Replace `cloud.example.com` and the key. **Claude Code** ```bash claude mcp add --transport http cloud https://cloud.example.com/mcp \ --header "Authorization: Bearer vk_XXXX" ``` **Cursor** (`~/.cursor/mcp.json` or `.cursor/mcp.json`; set `CLOUD_API_KEY` in the environment Cursor starts from): ```json { "mcpServers": { "cloud": { "url": "https://cloud.example.com/mcp", "headers": { "Authorization": "Bearer ${env:CLOUD_API_KEY}" } } } } ``` **Claude Desktop** (needs Node.js 18+; `claude_desktop_config.json`), through the `mcp-remote` bridge: ```json { "mcpServers": { "cloud": { "command": "npx", "args": ["-y", "mcp-remote", "https://cloud.example.com/mcp", "--header", "Authorization:${CLOUD_AUTH_HEADER}"], "env": { "CLOUD_AUTH_HEADER": "Bearer vk_XXXX" } } } } ``` Write the header without a space after the colon and put the whole value in the environment variable, as above. 3. Restart the client and ask it to list your tools. Check the connection without a client: ```bash curl -s https://cloud.example.com/mcp \ -H "Authorization: Bearer vk_XXXX" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` ## How it works | Item | Detail | |---|---| | Endpoint | `POST /mcp` with a JSON-RPC 2.0 message; the answer is `application/json`. `GET /mcp` answers 405 (no server-to-client stream); `DELETE /mcp` ends the session. | | Authentication | Only `Authorization: Bearer vk_...` (session tokens, cookies and admin keys are refused with 401 and a `WWW-Authenticate: Bearer` header). | | Session | `Mcp-Session-Id` is issued by `initialize`, bound to that key, valid 24 hours; at most 16 sessions per key. | | Tools | One per public API operation visible to the key. The tool name is the CLI command with `_` instead of spaces and dashes (`vm firewall add` becomes `vm_firewall_add`) and equals the OpenAPI `operationId`. A tool is listed only when the key has its scope and the module is enabled. | | Capabilities | Tools only (no resources or prompts). | | Size | Request body up to 1 MB; tool result text up to about 100 KB (the rest is cut with a note); a call takes at most 55 seconds. | ### Safety rules - **Same gate as REST.** Each `tools/call` goes through the panel's own request pipeline: scope, site, IP restriction, rate limit, idempotency, ownership checks (404 for other people's resources) and quotas all apply. MCP cannot grant anything the key does not have. - **Confirmation.** Tools that cost money, delete, or return credentials (console tickets, kubeconfig, S3 or SSH secrets, password resets, new keys) require the argument `confirm: true`. The tool description tells the assistant to pass it only after the user has approved, so an instruction hidden in a web page or log cannot trigger it on its own. - **Idempotency.** Paid tools accept `idempotency_key`; if omitted a stable key is derived from the tool name and arguments, so an identical retry cannot charge twice. - **Not exposed as tools:** streaming endpoints (the AI Agent chat), one-time SSH or console WebSocket tickets, binary downloads, and the VPN app endpoints (`/v1/ext/*`). - **Never possible:** the API never allows top-ups, password changes, deleting the account or creating keys, so no assistant can do them. ### Limits | Limit | Default | |---|---| | Tool calls per key | 60 per minute | | MCP requests per key | 240 per minute | | Plus the normal key and account limits | See [REST API](https://nexcmp.com/docs/developers/api/#rate-limits) | Over the limit the endpoint answers HTTP 429 with a JSON-RPC error and `Retry-After`. ## Verify 1. The `curl` command above returns a JSON-RPC result with a `tools` list. Only tools matching the key's scopes appear. 2. In your client ask "list my VMs": the assistant calls `vm_list` and shows the result. 3. In the portal the key's **Last used** time moves, and write calls appear in the **Key usage log**. ## Limits & notes - **Browser sign-in (OAuth).** The reference page also describes signing in through the browser for clients that support MCP OAuth. In an installed NexCMP the supported, tested method is the API key (or `cmp login` for the CLI). - **Reseller sites** do not show the MCP section; their customers use the reseller's domain for the API. - **Least privilege.** Create a dedicated key per assistant with read scopes first; add write scopes only when needed. - Revoke the key in **API keys** to cut the assistant's access at once. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | 401 with `WWW-Authenticate: Bearer` | No key, wrong key, or a session token | Send `Authorization: Bearer vk_...`. | | 403 about `Origin` | A browser page on another origin | Use a desktop or CLI client; or ask your administrator to allow the origin (`mcp_allowed_origins`). | | 404 on a request with `Mcp-Session-Id` | Session belongs to another key or expired | Run `initialize` again. | | 405 on `GET /mcp` | Expected: the server has no event stream | Use POST only (clients do this automatically). | | The tool list is short | Key scopes or modules | Add scopes to the key; enable the module. | | Tool answers it needs `confirm: true` | Paid, delete or sensitive tool | The assistant must set `confirm` after you approve. | | 429 | MCP or key rate limit | Wait for `Retry-After`. | | Client cannot connect with a header | Client without custom-header support | Use the `mcp-remote` bridge. | --- # Configure with AI > **Where in the admin:** AI Chat (`/agent`) and System › Settings › API & AI agents (`/agent/keys`). Everything on these pages is for administrators only. ## What it does From NexCMP 1.4.0 you can ask an AI assistant to configure the panel for you: connect Proxmox VE or CloudStack, create a zone, build a plan table from your costs, set up email, payments and branding, find a customer, or export the whole configuration. The assistant works through a fixed set of **admin tools**. It never gets a shell, never reads files on the server and never sees a stored password or API key. Use it when you want a first setup in minutes instead of clicking through a dozen pages, or when you manage several installations and want to keep their configuration in a file. ## Three ways to use it | Way | Who talks to the AI | Where it runs | Start here | |---|---|---|---| | **AI Chat in the admin** | You, in the NexCMP admin | The AI source you choose: an API key from OpenAI, Anthropic, Gemini or OpenRouter, your panel's own AI provider, or an official AI CLI you signed in to | [AI Chat in the admin](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/) | | **Your own AI client over MCP** | You, in Claude Code, Codex CLI, Gemini CLI, Cursor, Claude Desktop, claude.ai or ChatGPT | On your machine or in the vendor's app, connected to your panel at `https://cloud.example.com/mcp/admin` | [Connect an AI client](https://nexcmp.com/docs/configure-with-ai/mcp-admin/) | | **Configuration as code** | You or a script | A YAML file and the `nexcmp config` command on the server (or the Import/Export page) | [Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/) | All three call the same tools and pass through the same safety layer, so you can mix them: draft a configuration with an assistant, export it as YAML, review it in your editor and apply it later. ## Safety model | Rule | What it means for you | |---|---| | **Scopes** | A key or connection is limited to the scopes you tick (`admin:read`, `catalog:write`, `infra:write`, `billing:write`, `customers:write`, `settings:write`, `ai:write`, `secrets:write`). Without a scope the call is refused. See [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#scopes). | | **Expiry** | Every key and connection expires: 1 to 90 days for keys you create, 30 days by default. OAuth access tokens last one hour. | | **IP restriction and rate limit** | A key can be limited to IP ranges and to a number of calls per minute (120 by default). | | **Two-step confirmation** | Actions that move money, delete data, change payment credentials or change sign-in security return a preview first and do nothing until you approve. Inside AI Chat every change is a confirmation card you press yourself. | | **Things keys can never do** | Create administrators, change 2FA, create or revoke keys, log in as a customer, back up, restore or update the panel, and similar. These need you in the admin. | | **Activity log** | Every call, allowed or refused, is recorded with the key, the client, the tool, the IP and the result. See System › Settings › API & AI agents › Activity. | | **Secrets are never returned** | Passwords, API keys and tokens can be set but never read back. The answers the AI sees show them masked. | | **Demo installations are read-only** | On a demo panel every write is refused with `demo_read_only`. | ::: important What the AI can read With `admin:read` an assistant can read customer names, e-mail addresses, balances and billing records. When you use an AI source that runs at a vendor (OpenAI, Anthropic, Google, OpenRouter) that data is sent to that vendor to answer your question. Give an assistant the smallest set of scopes it needs, and choose a source you trust with that data. ::: ## Quick start 1. Sign in to the admin and open **AI Chat**. If no AI source is connected yet, open **System › Settings › AI accounts** and connect one (see [AI accounts and subscriptions](https://nexcmp.com/docs/configure-with-ai/ai-accounts-and-subscriptions/)). 2. Type "Check my setup progress" and press Enter. The assistant reads the [Setup progress checklist](https://nexcmp.com/docs/configure-with-ai/setup-checklist/) and tells you what is left. 3. Ask it to do the next step. Review each confirmation card and press **Confirm** to apply it. For a first look at what to ask, see [Example prompts](https://nexcmp.com/docs/configure-with-ai/example-prompts/). ![AI Chat page with a conversation, a tool result and a confirmation card](https://nexcmp.com/docs/images/configure-with-ai/overview-01-ai-chat.webp) ## Limits & notes - **Demo and read-only.** The pages are visible on a demo panel, but nothing can be saved there. - **No shell.** The assistant can only call the admin tools listed in [Connect an AI client](https://nexcmp.com/docs/configure-with-ai/mcp-admin/#tools). It cannot run commands on the server. - **Network and routing modules** (BGP, XDP, interfaces, NAT and similar) are read-only for AI, and sensitive areas such as administrator accounts, backups and updates are closed to keys entirely. - **Prices** are in USD: plan prices in dollars per hour, balances in cents. - **Cost.** AI answers are paid to the AI source you connect (API usage, or your own subscription). In AI Chat you can set a daily spending cap. For the NexCMP license model see https://nexcmp.com/pricing. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | AI Chat says no AI source is connected | No source set up for your admin account | Open System › Settings › AI accounts and connect one. | | An assistant answers `insufficient_scope` | The key or connection lacks the scope for that tool | Create a key with the needed scope, or reconnect and tick it on the consent screen. See [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#errors). | | Every change shows a confirmation card | Expected in AI Chat | Press **Confirm** to apply or **Reject** to cancel. | | Writes fail with `demo_read_only` | The panel is in demo mode | Use a non-demo installation. | --- # AI Chat in the admin > **Where in the admin:** AI Chat (`/agent`, its own menu entry) and the floating **Ask AI** button on every admin page. The answer sources are set up in System › Settings › AI accounts (`/agent/accounts`). ## What it does AI Chat is an assistant built into the admin. You describe what you want ("connect my Proxmox cluster", "set up email"), the assistant reads the current state with the admin tools, proposes changes and shows each one as a **confirmation card**. Nothing is applied until you press **Confirm**. The **Ask AI** panel is the same chat in a narrow side panel, so you can ask while you look at a page. Buttons such as **Do it for me** (in the [Setup progress checklist](https://nexcmp.com/docs/configure-with-ai/setup-checklist/) and in empty lists) open it with a ready question. It is not the customer-facing [AI Chat](https://nexcmp.com/docs/ai/ai-chat/) and [AI Agent](https://nexcmp.com/docs/ai/ai-agent/), which serve your customers. ## Requirements | Item | Requirement | |---|---| | Role | A signed-in administrator. Chats, keys and preferences belong to your admin account; other administrators do not see them. | | An answer source | At least one connected: an API key, a sign-in, the panel's own AI provider, or an AI CLI you signed in to. See [AI accounts and subscriptions](https://nexcmp.com/docs/configure-with-ai/ai-accounts-and-subscriptions/). | | Demo | On a demo panel you can read old chats but not send messages. | ## Choose where answers come from Pick the source in the selector under the input (it lists the sources you have connected). **System › Settings › AI accounts** also lets you set a default source, a default model and the daily cap. | Source | How you connect it | Notes | |---|---|---| | OpenAI (ChatGPT models) | Paste an API key from platform.openai.com | Billed by OpenAI per use. | | Anthropic (Claude) | Paste an API key from console.anthropic.com | Billed by Anthropic per use. | | Google Gemini | Paste an API key from aistudio.google.com, or **Sign in with Google** | Signing in with Google needs an OAuth client that **you** create in your own Google Cloud project. | | OpenRouter | Paste a key, or **Sign in with OpenRouter** | One account for many models. | | Panel AI provider | Already set in System › Settings › AI provider | The same provider your customers' AI Chat uses. [AI provider](https://nexcmp.com/docs/ai/ai-providers/). | | Claude, ChatGPT or Gemini subscription | Official CLI installed and signed in on the panel server | See [AI accounts and subscriptions](https://nexcmp.com/docs/configure-with-ai/ai-accounts-and-subscriptions/). | Keys you paste are stored encrypted for your admin account only. They are never shown again and another administrator cannot decrypt them. ## Use the chat 1. Open **AI Chat** (or click **Ask AI**). 2. Click **New chat**, or open an earlier one from the list on the left. 3. Type your request and press Enter (Shift+Enter starts a new line). You can attach images or text files (up to 4 per message, 3 MB each) to show an error or paste a document. 4. Watch the answer stream in. Each tool the assistant calls appears as a collapsible card with the tool name, its arguments and a short result. 5. Press **Stop** to cancel a running answer. Only one answer runs at a time per administrator. ![AI Chat with the conversation list, an answer containing tool cards and the input with source selector and Secret field](https://nexcmp.com/docs/images/configure-with-ai/ai-chat-in-admin-01-chat.webp) Up to 10 tool rounds happen per message. The assistant remembers the last 30 messages of the chat. Results from tools are passed to the model marked as data, not as instructions. ## Confirmation cards In AI Chat **every change is a card**, not only money actions. The assistant proposes; you decide. A card shows: - A title that says why it needs attention, for example "changes a customer balance". - The call: method and path. - A table of the fields that change: the current value and the new one. Secret values appear as `••••`. - A countdown (cards expire after 5 minutes). - **Confirm** and **Reject**. **Confirm** makes the panel itself apply the change, then tells the assistant what happened. The assistant cannot press the button, cannot approve through the API, and is told never to claim a change was applied before it was confirmed. If you reload the page, cards that are still valid come back. They also appear at the top of **System › Settings › API & AI agents** under "Waiting for your confirmation". ![Confirmation card showing the balance change, the countdown and the Confirm and Reject buttons](https://nexcmp.com/docs/images/configure-with-ai/ai-chat-in-admin-02-confirm.webp) ## Secrets {#secrets} Passwords and API keys must not go through the chat text. Use the **Secret** field (lock icon) next to the input: 1. Click the lock icon and enter a **name** (letters, digits and underscore, for example `smtp_password`) and the **value**. 2. In your message refer to the name: "use the secret smtp_password as the SMTP password". 3. The assistant only ever sees a placeholder that stands for the secret, written like this: ```text {{secret:smtp_password}} ``` The panel replaces it with the real value at the moment it runs the tool. The value is not saved in the chat history and is not sent to the AI source. If you paste something that looks like a key into the message by mistake, the panel moves it into the same protected store and masks it in the history, but treat that as a safety net, not a method. Secrets stay in the panel's memory only, for the conversation they were entered in and for up to one hour after its last message; a panel restart also clears them. Enter them again if the assistant asks. Setting a secret also needs the `secrets:write` permission for the chat session (see below). ## Permissions of the chat Each message runs with a temporary key that exists for the length of the answer (at most 15 minutes), works only from the panel server and can only be used through the confirmation cards. Its scopes are the ones ticked in **System › Settings › AI accounts › Chat session permissions**. `admin:read` is always on. All scopes except `secrets:write` are on by default; tick `secrets:write` if you want the assistant to be able to set passwords and keys (through the Secret field). On a demo panel the chat is read-only. ## Daily spending cap AI Chat adds up the estimated cost of answers from API sources. The default cap is **5 USD per administrator per day**. Set another amount (0 means no limit) under **AI accounts › Defaults for AI Chat**. The page shows "used today". When the cap is reached the chat refuses new messages (`daily_cap`) until the next day or until you raise the cap. Sources that are AI CLI subscriptions are not counted, because they are billed by your subscription. ## Verify 1. In **AI accounts** click **Test** on the source. It answers with the model name and the time in milliseconds. 2. In AI Chat ask "Check my setup progress". The answer lists the steps from the [checklist](https://nexcmp.com/docs/configure-with-ai/setup-checklist/) and a `setup_checklist` tool card appears. 3. Ask for a small change, for example "Set the brand name to Example Cloud". A confirmation card appears; press **Confirm** and check the Branding page. 4. **API & AI agents › Activity** lists the calls with the channel `chat`. ## Limits & notes - The assistant has no shell and cannot read files on the server. It only calls the [admin tools](https://nexcmp.com/docs/configure-with-ai/mcp-admin/#tools). - Changes that keys can never make ([list](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#what-a-key-can-never-do)) cannot be made from AI Chat either. - Check the model's work. A confirmation card shows exactly what will be sent; read it, especially prices and amounts. - API answers are billed by the AI vendor. The cap is an estimate; set a spending limit at the vendor too. - A CLI answer stops after 5 minutes. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | "Today's AI spending cap is reached" (`daily_cap`) | You used the daily cap | Raise it in AI accounts, or wait for tomorrow. | | "Another answer is still running - wait or press Stop" (`busy`) | One answer at a time per administrator | Press **Stop** or wait. | | The source does not appear in the selector | It is not connected, or (Claude CLI) the "I understand" switch is off | Connect it in AI accounts. | | "that does not look like an API key" | The pasted key has spaces or is too long | Paste only the key. | | Test fails | Wrong key, no credit, or the vendor is down | Check the key and billing at the vendor. | | The assistant says it cannot do something | The chat session lacks the scope, or the action is closed to AI | Tick the scope in AI accounts or do it in the admin. | | A card says it expired | More than 5 minutes passed | Ask the assistant to propose the change again. | | CLI source reports "usage limit" or "not logged in" | Your subscription limit or session | Sign in again or wait; see [AI accounts and subscriptions](https://nexcmp.com/docs/configure-with-ai/ai-accounts-and-subscriptions/). | --- # AI accounts and subscriptions > **Where in the admin:** System › Settings › AI accounts (`/agent/accounts`), tabs **Accounts** and **Use your own AI subscription**. The optional terminal is AI Console (`/agent/console`), which appears under AI Chat only when it is turned on. ## What it does AI Chat needs something to answer with. This page lets you connect that source for your own admin account: - **API sources**: OpenAI, Anthropic (Claude), Google Gemini and OpenRouter, with an API key or a sign-in. - **The panel AI provider**, if you already set one under System › Settings › AI provider. - **Your own AI subscription** (Claude, ChatGPT, Gemini) through the vendor's **official command-line tool**, either on the panel server or on your own computer. Everything you connect belongs to your admin account. Keys and tokens are stored encrypted for you alone; another administrator cannot use them. ## Requirements | Item | Requirement | |---|---| | Role | A signed-in administrator. On a demo panel nothing can be connected or removed. | | API sources | An account and an API key at the vendor (the card has a **Get API key** link that opens the vendor page). | | CLI on the panel server | AI Console turned on (see below), outbound HTTPS from the server to the vendor and to `nodejs.org` and the npm registry, and **2 GB of RAM or more** (a CLI needs about 1 GB free while it runs; the installer warns below 2 GB). Node.js 20 or newer is used if present, otherwise the installer downloads Node.js 22 with a checksum check. | | CLI on your computer | Node.js 20 or newer to install the CLI, and your own subscription. | ## Connect an API source 1. Open **System › Settings › AI accounts** and stay on **Accounts**. 2. On the card of the source click **Connect**. The vendor's key page opens in a new tab; create a key there. 3. Paste the key into the field and save. The card shows a connected state with the last four characters. 4. Choose the **default model** if you want a different one, then click **Test**. A good result reads like "Connected - example-model, 850 ms". ![AI accounts page with the API source cards, a connected OpenAI card and the Panel AI provider card](https://nexcmp.com/docs/images/configure-with-ai/ai-accounts-and-subscriptions-01-accounts.webp) | Source | Sign-in alternative | |---|---| | OpenRouter | **Sign in with OpenRouter** opens OpenRouter in a popup or tab; approve and you return to this page connected. | | Google Gemini | **Sign in with Google** appears after you have saved, under **Set up Google sign-in (optional)** on the card, an OAuth client created in **your own** Google Cloud project (client ID ending in `.apps.googleusercontent.com`, plus its secret). NexCMP ships no shared Google client. | | Panel AI provider | Uses the provider already configured under System › Settings › AI provider ([AI provider](https://nexcmp.com/docs/ai/ai-providers/)). Nothing to paste. | To disconnect a source click **Remove** on its card. It deletes the stored key for your account. ### Defaults, daily cap and permissions At the bottom of the **Accounts** tab, **Defaults for AI Chat** has three settings: | Setting | Meaning | |---|---| | Default source and model | What new chats use. You can still change the source per message. | | Daily spending cap (USD, 0 = no limit) | Default 5. The page shows "used today". See [AI Chat in the admin](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/#daily-spending-cap). | | Chat session permissions | The scopes the assistant may use. `admin:read` is always on; `secrets:write` is off by default. See [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#scopes). | Click **Save defaults**. ## Use your own AI subscription You can drive NexCMP with a Claude, ChatGPT or Gemini plan you already pay for, without a separate API key. There are two ways. ### Way 1 (recommended): the CLI on your own computer You use the vendor's own tool on your own computer and sign in with your own account in the vendor's normal way. NexCMP only receives MCP calls signed with a short-lived key. Open **AI accounts › Use your own AI subscription**; it shows three steps per tool. 1. Click **Create a 7-day key** and choose the level: **Read only** (`admin:read`) or **Read + configuration** (`admin:read`, `catalog:write`, `infra:write`, `settings:write`). The commands below then contain the real key. 2. Install the CLI and sign in. The key is shown only on this page and only once. | Tool | Install | Sign in | |---|---|---| | Claude Code | `npm install -g @anthropic-ai/claude-code` | run `claude`, then `/login` | | Codex CLI | `npm install -g @openai/codex` | `codex login` | | Gemini CLI | `npm install -g @google/gemini-cli` | run `gemini`, choose "Login with Google" | 3. Connect it to your panel with one command or one block, copied from the page: ```bash # Claude Code claude mcp add --transport http nexcmp https://cloud.example.com/mcp/admin \ --header "Authorization: Bearer ak_XXXX" ``` ```toml # Codex CLI: ~/.codex/config.toml, then: export NEXCMP_KEY=ak_XXXX [mcp_servers.nexcmp] url = "https://cloud.example.com/mcp/admin" bearer_token_env_var = "NEXCMP_KEY" ``` ```json // Gemini CLI: ~/.gemini/settings.json { "mcpServers": { "nexcmp": { "httpUrl": "https://cloud.example.com/mcp/admin", "headers": { "Authorization": "Bearer ak_XXXX" } } } } ``` 4. Start the tool and ask, for example, "what is left to set up in nexcmp?". All details of the MCP connection are in [Connect an AI client](https://nexcmp.com/docs/configure-with-ai/mcp-admin/). The same tab shows the URL for the **claude.ai** and **ChatGPT** connectors, which sign in with OAuth and need no key: [OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/). ![Use your own AI subscription tab with the 7-day key button and install, sign in and connect commands for three CLIs](https://nexcmp.com/docs/images/configure-with-ai/ai-accounts-and-subscriptions-02-subscription.webp) ### Way 2: the CLI on the panel server (Install / Login buttons) The cards under "Subscription through a CLI (Claude, ChatGPT, Gemini)" let NexCMP install the official CLI on the panel server and open its own sign-in flow, so **AI Chat** can answer with your subscription. **How it is isolated.** The CLIs run in a separate process (`nexcmp-ai.service`) as their own system user `nexcmp-ai`, without sudo, with no access to NexCMP's data and configuration folders and a memory limit. For every AI Chat message the CLI is started with only the NexCMP MCP tools allowed (shell and file tools are off) and a key that works from the server only. NexCMP does not read or copy the CLI's sign-in: the CLI keeps it in a private folder for your admin account. There is nothing to turn on: the panel sets up the `nexcmp-ai` user and service when it is installed or updated to 1.4.0, so the cards are ready to use. **Install and sign in (per administrator):** 1. On the card of the CLI click **Login**. If the CLI is not installed yet, the panel installs it first: a progress bar and a short log show the download (Node.js if needed, then the CLI). A warning appears when the server has less than 2 GB of RAM. 2. The card shows what the CLI asks for: - **Codex CLI**: click **Open the sign-in page** and type the one-time code the card displays. (OpenAI's device sign-in must be enabled in your ChatGPT security settings.) - **Claude Code and Gemini CLI**: click **Open the sign-in page**, sign in, then paste the verification code the vendor's page shows into **Paste the verification code here** and click **Send code**. 3. The card turns to "Signed in" with the account and version. A sign-in that is not finished within 10 minutes is cancelled; click **Login** again. 4. In **AI Chat** the source now appears in the selector (for example "ChatGPT (via Codex CLI)"). 5. **Sign out** removes the CLI's sign-in for your account. For Claude the card has an extra switch, **I understand and accept the vendor's terms for using a subscription this way**, off by default. The source is not offered in AI Chat until it is on. ![CLI subscription cards with Install and Login, a one-time code on the Codex card and the I understand switch on the Claude card](https://nexcmp.com/docs/images/configure-with-ai/ai-accounts-and-subscriptions-03-cli.webp) ### Terms of the AI vendors Signing in with a consumer subscription is governed by each vendor's terms, which change. The notes below are a summary of what the vendors publish as of October 2026, not legal advice and not a statement on the vendor's behalf. Read the current pages before you turn this on. | Vendor | What the vendor's page covers | Source | |---|---|---| | Anthropic (Claude) | Signing in to the unmodified Claude Code with your own plan is covered. Using a subscription through other products, or having a product collect or pass on the sign-in, is treated differently, and plan limits assume ordinary individual use. This is why the Claude card carries a note and the **I understand** switch, and why Way 1 is the cleaner choice. | https://code.claude.com/docs/en/legal-and-compliance | | OpenAI (Codex) | Codex supports signing in with a ChatGPT account or an API key. OpenAI recommends API keys for automation and says not to expose Codex in untrusted or public environments. Use the card only for yourself. | https://developers.openai.com/codex/auth | | Google (Gemini CLI) | Supports Sign in with Google, an API key and Vertex AI; the terms depend on how you sign in, and your plan's quotas apply. | https://geminicli.com/docs/get-started/authentication | If you want to avoid the question entirely, use an **API key** source, or Way 1 on your own computer. ## AI Console AI Console is an optional terminal page for running one of the signed-in CLIs on the panel server. It is **off by default**. - Turn it on or off, and manage the CLIs, on the server: ```bash sudo nexcmp ai-console enable # turn on (also creates the nexcmp-ai user and service) sudo nexcmp ai-console disable # turn off sudo nexcmp ai-console status # runner, console state and installed CLIs sudo nexcmp ai-console install claude # or codex, or gemini ``` `status` prints whether the AI runner socket is ready, whether AI Console is enabled or disabled, and for each CLI "installed" or "not installed". `sudo nexcmp status` also shows "AI runner: active". - Once enabled, an **AI Console** tab appears under AI Chat. Pick a signed-in CLI, re-enter your admin password (and your 2FA code if you use it) and open a session. A banner reminds you that commands here can only manage NexCMP through its MCP tools; shell and file tools are disabled. - Demo installations never create the `nexcmp-ai` user. ![AI Console start screen with the CLI selector, password field and the MCP-only warning banner](https://nexcmp.com/docs/images/configure-with-ai/ai-accounts-and-subscriptions-04-console.webp) ## Verify 1. **Test** on an API source returns the model name. 2. On the CLI card the state reads "Signed in" with the account. 3. On the server: `sudo nexcmp ai-console status` lists the CLI as installed, and `systemctl is-active nexcmp-ai` prints `active`. 4. In AI Chat choose the source, ask "Check my setup progress", and a tool card appears. ## Limits & notes - A CLI answer is limited to 5 minutes and one answer at a time per administrator. The daily cap does not count CLI answers. - Every change from a CLI source is still a confirmation card in AI Chat; the CLI cannot apply changes by itself. - Subscription plans have their own usage limits. When the vendor says "usage limit" or "not logged in" the chat shows that message. - Removing a source does not delete conversations. - Keep the server patched: the CLIs are installed from the vendors' public packages at the time you click Install. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Login is disabled and the card says "AI tools are not ready on the server yet" | The `nexcmp-ai` service is not running | Update the panel to the latest version, then check `sudo nexcmp status`. On the read-only demo this is expected. | | "AI tools are not installed on this server yet" | The `nexcmp-ai` service is not running (older panel, or a demo panel) | Update the panel, then `sudo nexcmp ai-console install claude` (or `codex`, `gemini`). | | Warning about RAM below 2 GB | The server is small | Install may be slow or fail; add memory or use Way 1. | | "Node.js download checksum mismatch" or "download failed" | The server cannot reach nodejs.org, or a proxy altered the download | Allow outbound HTTPS and retry. | | Install ends in an error | npm could not install the package | Read the log on the card, check outbound access to the npm registry, retry. | | Login ends in an error or times out | The 10-minute window passed, or the code was wrong | Click **Login** again and complete it faster. | | The Claude source is missing in AI Chat | The **I understand** switch is off, or the CLI is not signed in | Turn the switch on after reading the vendor's page, then sign in. | | "Sign in with Google" is missing on Gemini | No Google OAuth client saved yet | Create the client in your own Google Cloud project and save it under **Set up Google sign-in (optional)** on the card, or paste an API key instead. | | "Sign in with OpenRouter" returns an error | You cancelled, or the popup was blocked | Allow popups and try again. | | The CLI answer says "usage limit" | The plan's quota is used up | Wait, or switch source. | --- # Admin API keys > **Where in the admin:** System › Settings › API & AI agents (`/agent/keys`), tab **API keys**. The same page has the tabs **Connect an AI**, **Connected apps** and **Activity**. ## What it does An admin API key (prefix `ak_`) lets an AI client or a script manage your panel with exactly the permissions you give it. It works on the admin API under `/v1/admin/` and on the admin MCP endpoint `/mcp/admin`. It does not work anywhere else. ::: info Not the same as customer keys Customers create keys that start with `vk_` in the customer portal ([API keys and scopes](https://nexcmp.com/docs/developers/api-keys/)). Those only reach the customer API and `/mcp`. An `ak_` key is refused on the customer API, and a `vk_` key is refused on the admin API. ::: ## Requirements | Item | Requirement | |---|---| | Role | You must be signed in to the admin. Keys cannot create other keys. | | Demo | On a demo panel the page is read-only: no key can be created or revoked. | | Reachability | The client must reach `https://cloud.example.com/mcp/admin` or `/v1/admin/...` on your panel (check your firewall if you use IP restrictions). | ## Create a key 1. Open **System › Settings › API & AI agents** and stay on the tab **API keys**. 2. Click **Create new key**. 3. Fill in the form: | Field | Rules | |---|---| | Name | Up to 60 characters, for example "Claude Code on my laptop". | | Scopes | Tick at least one. See [Scopes](#scopes). Start with `admin:read` only. | | Expiry | 1, 7, 30, 60 or 90 days. Default 30. Maximum 90. A key without an expiry cannot be created. | | IP ranges (optional) | One IPv4 or IPv6 address or CIDR per line, for example `203.0.113.0/24` or `2001:db8::/48`. At most 32. Leave empty to allow any IP. | | Calls per minute | 1 to 6000. Default 120. | 4. Click **Create key**. A box shows the key once, for example `ak_XXXX`. **Copy it now**: NexCMP stores only a hash and can never show it again. 5. Click **I have saved the key**. Open the **Connect an AI** tab to get ready-to-paste commands that already contain the key you just made (only on this page, until you reload it). ![New key dialog with name, scope checkboxes, expiry and IP range filled in](https://nexcmp.com/docs/images/configure-with-ai/admin-api-keys-01-create.webp) ![Dialog showing the new key once with a Copy button and a warning](https://nexcmp.com/docs/images/configure-with-ai/admin-api-keys-02-shown-once.webp) ## Scopes A key can only use the tools and endpoints its scopes allow. Write scopes do not include read access: a key that should read and write needs `admin:read` as well. | Scope | What it allows | |---|---| | `admin:read` | Read settings, catalog, customers, billing and logs. Secrets are never returned. | | `catalog:write` | Plans, OS templates, quotas, discount codes. | | `infra:write` | Infrastructure sources (CloudStack, Proxmox), zones, servers. | | `billing:write` | Payment gateways, currency, invoices, balances. Money actions need confirmation. | | `customers:write` | Customers and resellers. | | `settings:write` | General settings, branding, email, legal pages, modules, notifications. | | `ai:write` | The AI provider and AI features. | | `secrets:write` | Write passwords and API keys. They can be set but never read back. | ::: tip Setting a secret needs two scopes Any call that carries a password, API key or token value (an SMTP password, a payment key, a Telegram bot token, an AI provider key, CloudStack or Proxmox credentials) needs `secrets:write` **and** the scope of the area (for example `settings:write` for SMTP). If you would rather type secrets yourself, leave `secrets:write` off and enter them in the admin. ::: ## Revoke a key 1. Find the key in the table (status **Active**, **Expired** or **Revoked**; **Last used** shows the time and the client). 2. Click **Revoke** and confirm. The key stops working at once. Revoking is permanent; create a new key if you need one again. ## What a key can never do A key can never reach these areas, whatever its scopes. They need you signed in to the admin. - Create or change administrator users, 2FA and sign-in security, admin sessions, and the admin API tokens. - Create, list or revoke admin keys, OAuth connections and AI accounts, or use the AI Console. - Back up or restore the panel, update it, or change its license. - Log in as a customer or a reseller, open a customer's console, read VM credentials or kubeconfig, set SSH keys, or send or reset passwords. - Change network and routing modules (BGP, FRR, RTBH, XDP, interfaces, static and policy routes, NAT) or external agents and portal integration. These are read-only. - Change settings such as `demo_*`, `admin_*`, host lists, passkey, re-authentication and captcha secrets. Anything not covered by a rule is closed by default for writes, and readable with `admin:read` for reads. ### Actions that need your confirmation These succeed only after you approve a preview (see [Two-step confirmation](#two-step-confirmation)): - Changing a customer balance or a reseller wallet, paying out money, billing records, the exchange rate. - Changing prices (plan prices, bulk plan creation, image and storage pricing) and creating discount or money codes. - Saving payment gateway credentials or the CloudStack connection. - Deleting anything, blocking or deleting a customer, changing a customer's limits, suspending or closing a reseller. - Acting on customer servers, or creating a paid server for a customer. - Changing site-wide switches, sign-in security settings or payment settings through general settings. ## Two-step confirmation When a call needs confirmation, nothing changes yet. The answer is HTTP **428** with `confirm_required`, a `confirm_token`, a `preview` (method, path, reason, the new values and, when available, the current values) and `expires_in: 300`. 1. The client shows you the preview. 2. After you approve, it repeats the **same** request (same method, path and body) with the header `X-Confirm-Token: `. MCP tools take the argument `confirm_token` instead. 3. The token works once and expires after 5 minutes. Keys created inside the admin for AI Chat can only be confirmed on screen: the answer says `confirm_in: "ui"` and a confirmation card appears in the admin. The AI cannot approve it. ## Rate limit and IP rules - Over the per-minute limit a call returns `rate_limited` (HTTP 429) with `Retry-After: 60`. - With IP ranges set, calls from any other address return `ip_not_allowed` (HTTP 403). ## Activity log The **Activity** tab lists every call made with a key, newest first: time, key, client, how it arrived (`http`, `mcp`, `chat`, `cli`, `confirm`, `preview`), method and path, tool, HTTP status, IP and the (masked) details. You can search by path, tool or key name, show only errors (HTTP 400 and above) or only calls waiting for confirmation (428 and above), and limit the period to the last hour, 24 hours or 7 days. NexCMP keeps the latest 200,000 entries. Changes made through a key also appear in Logs › Admin actions under the name `ak:/`. ![Activity tab listing calls with status, client, tool and IP](https://nexcmp.com/docs/images/configure-with-ai/admin-api-keys-03-activity.webp) ## Verify Test a read-only key from any machine that is inside the IP range: ```bash curl -s https://cloud.example.com/v1/admin/agent/tools \ -H "Authorization: Bearer ak_XXXX" ``` You get a JSON list of the tools your scopes allow. Run one: ```bash curl -s -X POST https://cloud.example.com/v1/admin/agent/tools/system_health \ -H "Authorization: Bearer ak_XXXX" \ -H "Content-Type: application/json" -d '{}' ``` The key's **Last used** time moves and the call appears in **Activity**. The full tool list and an OpenAPI description (`/v1/admin/openapi.json`) are described in [Connect an AI client](https://nexcmp.com/docs/configure-with-ai/mcp-admin/). ## Errors Errors are JSON: `{"error": {"code": "...", "message": "..."}}`. | HTTP | `code` | Meaning | Fix | |---|---|---|---| | 401 | `invalid_key` | The key is missing, malformed or unknown. Also returned when an `ak_` key is used on a non-admin path. | Check the key; use it only on `/v1/admin/` and `/mcp/admin`. | | 401 | `key_expired` | The key's expiry passed. | Create a new key. | | 401 | `key_revoked` | The key was revoked. | Create a new key. | | 403 | `ip_not_allowed` | The caller's IP is outside the key's IP ranges. | Call from an allowed IP, or create a key with the right range. | | 403 | `local_only_key` | A key made for AI Chat or the server CLI was used from another machine. | These keys only work on the panel server itself. | | 403 | `insufficient_scope` | The key lacks a scope; the answer includes `required_scope`. | Create a key with that scope. | | 403 | `forbidden_for_keys` | The endpoint, or the setting you tried to change, is closed to keys. | Do it in the admin. | | 403 | `demo_read_only` | The panel is in demo mode and the call writes. | Use a non-demo panel. | | 403 | `confirm_in_ui` | This action must be confirmed on screen in the admin. | Approve the confirmation card. | | 428 | `confirm_required` | A preview is ready; nothing changed. Includes `confirm_token`. | Show the preview, then repeat with `X-Confirm-Token`. | | 409 | `confirm_expired` | The token is older than 5 minutes. | Send the request again for a new preview. | | 409 | `confirm_mismatch` | The token belongs to a different request (method, path or body differs). | Repeat the identical request. | | 409 | `confirm_invalid` | Unknown or already used token. | Send the request again for a new preview. | | 429 | `rate_limited` | Too many calls this minute. | Wait for `Retry-After`. | | 400 | `invalid_path` | The path contains forbidden sequences. | Use a plain path. | | 413 | `too_large` | The body is over 10 MB. | Send less data. | | 400 | `invalid_argument` | A tool argument is missing, unknown or has the wrong type; the message names it. | Fix the argument. | ## Limits & notes - Keep keys short-lived and narrow. Prefer one key per client so you can revoke a single one. - A key is as powerful as its scopes. `secrets:write` plus `billing:write` is the most sensitive combination; money actions still stop at a preview. - Keys made automatically (for AI Chat, the server CLI) live for minutes only and work only from the panel server. - To cut off an AI client at once, revoke its key. OAuth connections are revoked on the **Connected apps** tab ([OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/)). --- # Connect an AI client (MCP) > **Where in the admin:** System › Settings › API & AI agents (`/agent/keys`), tab **Connect an AI** shows the endpoint URL and a ready-to-copy snippet for each client, filled with a key you just created. ## What it does The **Model Context Protocol (MCP)** lets an AI client call tools. Your panel serves an admin MCP server at `https://cloud.example.com/mcp/admin`. Connect your own AI tool to it and ask in plain language: "what is left to set up?", "add my Proxmox cluster", "create plans with a 40% margin". The client calls the admin tools below; the panel checks scopes, asks you before anything risky and records every call. ::: info Two MCP endpoints `/mcp` is the **customer** endpoint (customer API keys, `vk_`; see [MCP server](https://nexcmp.com/docs/developers/mcp/)). `/mcp/admin` is the **admin** endpoint described here (admin keys `ak_` or OAuth). The two never mix: admin tools are not visible on `/mcp`, and `/mcp/admin` accepts only admin keys. ::: ## Requirements | Item | Requirement | |---|---| | Endpoint | `https://cloud.example.com/mcp/admin` on your panel's own domain. Copy the exact URL from the **Connect an AI** tab. The same page lists a local URL (`http://127.0.0.1:/mcp/admin`) for tools running on the server itself. | | Credentials | An [admin API key](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/) (`ak_...`) with the scopes you want, **or** OAuth sign-in for web connectors ([OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/)). | | Client | Any MCP client that supports the **Streamable HTTP** transport and a custom `Authorization` header. Protocol versions 2025-11-25, 2025-06-18 and 2025-03-26. | | Network | If you put your own reverse proxy in front of the panel, it must forward `/mcp/admin`. Web connectors (claude.ai, ChatGPT) connect from the vendor's servers, so the URL must be reachable from the internet. | ## Prepare the infrastructure Nothing to install on the server. If you restrict the key by IP, make sure the address your client connects from is in the range. Test reachability first: ```bash curl -i -X POST https://cloud.example.com/mcp/admin ``` An answer of `401` with a `WWW-Authenticate: Bearer ... resource_metadata=...` header means the endpoint is up and wants credentials. ## Connect in NexCMP 1. Create a key: **System › Settings › API & AI agents › API keys › New key**. For a first try use `admin:read` only; add write scopes when you need them. See [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/). 2. Open the tab **Connect an AI**. While the "key created" dialog is still fresh, every snippet below already contains your real key. Otherwise it shows the placeholder `ak_YOUR_KEY`. 3. Copy the snippet for your client and run or paste it as described next. ![Connect an AI tab with the MCP URL and one card with a copy button per client](https://nexcmp.com/docs/images/configure-with-ai/mcp-admin-01-connect.webp) ### Claude Code ```bash claude mcp add --transport http nexcmp https://cloud.example.com/mcp/admin \ --header "Authorization: Bearer ak_XXXX" ``` Then start `claude` and ask "list the tools from nexcmp" or "what is left to set up?". Checked on 30/10/2026 with Claude Code 2.1: `claude mcp add --transport http …` writes the server (add `-s project` to keep it in the project's `.mcp.json` — Claude Code asks you to approve project servers the first time). For scripts, `claude -p "…" --mcp-config .mcp.json --strict-mcp-config --allowedTools "mcp__nexcmp__*"` runs without the approval prompt and lets Claude use only the panel's tools. ### Claude Desktop Needs Node.js 18 or newer. Add this to `claude_desktop_config.json` (it uses the `mcp-remote` bridge) and restart Claude Desktop: ```json { "mcpServers": { "nexcmp": { "command": "npx", "args": ["-y", "mcp-remote", "https://cloud.example.com/mcp/admin", "--header", "Authorization:Bearer ak_XXXX"] } } } ``` If your system mangles the space in the header argument, put the whole value in an environment variable instead, exactly as for the customer endpoint: `"args": [..., "--header", "Authorization:${NEXCMP_AUTH_HEADER}"]` with `"env": { "NEXCMP_AUTH_HEADER": "Bearer ak_XXXX" }`. ### claude.ai (custom connector) No key needed: it signs in with OAuth. In claude.ai open the connectors settings, add a custom connector, paste `https://cloud.example.com/mcp/admin`, then sign in with your NexCMP admin account and choose the permissions on the consent screen. Details: [OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/). ### ChatGPT (developer mode) No key needed. In ChatGPT turn on developer mode, add an MCP connector with the URL `https://cloud.example.com/mcp/admin`, choose OAuth and sign in with your NexCMP admin account. Details: [OAuth connectors](https://nexcmp.com/docs/configure-with-ai/oauth-connectors/). ### Codex CLI Add to `~/.codex/config.toml`: ```toml [mcp_servers.nexcmp] url = "https://cloud.example.com/mcp/admin" bearer_token_env_var = "NEXCMP_KEY" ``` Then export the key in the shell that starts Codex: ```bash export NEXCMP_KEY=ak_XXXX codex codex mcp list # shows nexcmp with "Bearer token" auth ``` ::: tip Non-interactive `codex exec` In `codex exec` (scripts, CI) Codex does not ask before calling tools, and it skips tools that change data. Add `default_tools_approval_mode = "approve"` under `[mcp_servers.nexcmp]` to let it call them; the panel still returns a preview and asks for confirmation for money, security and delete actions. Checked with Codex CLI 0.166 on 30/10/2026. ::: ### Gemini CLI Add to `~/.gemini/settings.json`: ```json { "mcpServers": { "nexcmp": { "httpUrl": "https://cloud.example.com/mcp/admin", "headers": { "Authorization": "Bearer ak_XXXX" } } } } ``` Gemini CLI turns MCP servers off in folders it does not trust. Start it in a folder you trust (or trust the folder when it asks), then check with `gemini mcp list` — it shows `nexcmp … Connected`. You can keep the key out of the file with `"Authorization": "Bearer $NEXCMP_KEY"`. Checked with Gemini CLI 0.66 on 30/10/2026. ### Cursor Add to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project): ```json { "mcpServers": { "nexcmp": { "url": "https://cloud.example.com/mcp/admin", "headers": { "Authorization": "Bearer ak_XXXX" } } } } ``` ::: tip Keep the key out of config files Where the client supports it, read the key from an environment variable instead of writing it in the file: Codex does this by default (`bearer_token_env_var`), and Cursor accepts `"Bearer ${env:NEXCMP_KEY}"`. Never commit a file that contains a key. ::: ### OpenAI Agents SDK (Python) ```python from agents.mcp import MCPServerStreamableHttp nexcmp = MCPServerStreamableHttp( params={"url": "https://cloud.example.com/mcp/admin", "headers": {"Authorization": "Bearer ak_XXXX"}}, name="nexcmp", ) ``` A complete run (checked with `openai-agents` 0.24 on 30/10/2026 — a read tool, then a balance change that returned the confirmation preview and was applied on the second call with `confirm_token`): ```python import asyncio from agents import Agent, Runner from agents.mcp import MCPServerStreamableHttp async def main(): async with MCPServerStreamableHttp(params={"url": "https://cloud.example.com/mcp/admin", "headers": {"Authorization": "Bearer ak_XXXX"}}, name="nexcmp") as nexcmp: agent = Agent(name="admin", model="gpt-5", mcp_servers=[nexcmp], instructions="You operate a hosting panel through the nexcmp tools. Ask me before confirming money actions.") result = await Runner.run(agent, "Run system_health and summarise it in one line.") print(result.final_output) asyncio.run(main()) ``` ### OpenAI Responses API Pass the server as a tool of type `mcp`. Keep `require_approval` on `always` so your program asks you before each call: ```json { "type": "mcp", "server_label": "nexcmp", "server_url": "https://cloud.example.com/mcp/admin", "headers": { "Authorization": "Bearer ak_XXXX" }, "require_approval": "always" } ``` ### Anything else (plain HTTP) A client without MCP can call the same tools over HTTP. `GET /v1/admin/agent/tools` lists them, `POST /v1/admin/agent/tools/` runs one with a JSON body of arguments, and `GET /v1/admin/openapi.json` returns an OpenAPI 3.1 description (each tool is one operation). All need `Authorization: Bearer ak_...`. ## Verify 1. Without a client, list the tools: ```bash curl -s https://cloud.example.com/mcp/admin \ -H "Authorization: Bearer ak_XXXX" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' ``` The answer is a JSON-RPC result with the tools your scopes allow. 2. In your client ask "run system_health". It returns alerts, the health of connected infrastructure, payments, email, AI and the license state. 3. Open **API & AI agents › Activity**: the calls are listed with the client name and `mcp`. 4. On the server, `sudo nexcmp status` prints a line "MCP admin" that says `ready` when the endpoint answers (HTTP 401 without a key is the healthy state). ## Tools The server offers 58 tools. A client only sees the tools its key's scopes allow: read tools need `admin:read`, write tools need their own scope. Write tools also get an optional `confirm_token` argument (see [Confirmation](#confirmation-confirm-token)). Arguments are checked against each tool's schema: unknown or mistyped arguments are rejected with a message that names them. Prices are in USD: plan prices in dollars per hour (`hourly_usd`), balances in cents. Secrets can be set but are never returned. ### Overview and logs | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `system_health` | Alerts that need attention, infrastructure health, payments, email, AI, license and update status. | `admin:read` | No | No | | `setup_checklist` | What is configured and what is left: license, infrastructure, zone, OS templates, plans, payments, email, branding, legal, test order. | `admin:read` | No | No | | `license_status` | License status and whether a newer NexCMP version exists. | `admin:read` | No | No | | `audit_search` | Search the admin audit log and the API-key log. | `admin:read` | No | No | ### Settings, branding, legal, modules | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `settings_get` | Read general settings (`keys` filters; a trailing `*` matches a prefix). | `admin:read` | No | No | | `settings_update` | Change general settings from a flat `values` object. Only keys listed by `settings_get` are accepted. | `settings:write` | Yes | When a key touches payments, sign-in security, site-wide switches or prices | | `branding_get` | Brand name, colour, display currency, seller name, logo and favicon URLs. | `admin:read` | No | No | | `branding_update` | Set brand name (up to 60 characters), colour (`#rrggbb`), display currency (`usd` or `vnd`) and seller name. | `settings:write` | Yes | No | | `branding_logo_set` | Upload a light or dark logo or a favicon from an https URL or base64 (PNG, JPEG, WEBP, ICO or SVG, up to 512 KB). | `settings:write` | Yes | No | | `legal_get` | Terms of service and privacy policy (English and Vietnamese) and the neutral defaults. | `admin:read` | No | No | | `legal_update` | Set terms and privacy text (simple markdown, no HTML, up to 50 KB each). An empty string restores the default. | `settings:write` | Yes | No | | `modules_get` | Which optional modules are on and which can be switched. | `admin:read` | No | No | | `module_set` | Switch an optional module on or off (`key` must be one of `settable`). | `settings:write` | Yes | No | ### Email and alerts | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `smtp_get` | Outgoing email settings (password masked) and queue status. | `admin:read` | No | No | | `smtp_configure` | Set the SMTP server. Changing host, port or security needs the password again. | `settings:write` (+ `secrets:write` for the password) | Yes | No | | `smtp_test` | Send a real test email with the saved settings. | `settings:write` | Sends an email | No | | `telegram_get` | Admin notification channels (chat ids; bot tokens are never returned). | `admin:read` | No | No | | `telegram_configure` | Add or update the Telegram alert channel (bot token and chat id) and switch admin alerts on or off. | `settings:write` (+ `secrets:write` for the token) | Yes | No | | `telegram_test` | Send a test message to the Telegram channel. | `settings:write` | Sends a message | No | ### Payments and currency | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `payments_get` | Status of Stripe, PayPal, crypto (USDT) and bank transfer, plus enabled channels and fees. | `admin:read` | No | No | | `payment_configure` | Set the credentials of one gateway (`stripe`, `paypal`, `crypto` or `bank`). | `billing:write` (+ `secrets:write`) | Yes | Always | | `payment_test` | Check a gateway's saved credentials with the provider. | `billing:write` | No | No | | `currency_get` | Display currency and the USD to VND rate (automatic or manual). | `admin:read` | No | No | | `currency_set` | Set the display currency and/or a manual rate (0 means automatic, otherwise 15000 to 50000). | `billing:write` | Yes | When you change the manual rate | ### Infrastructure and zones | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `cloudstack_get` | Saved CloudStack endpoint and zone and network ids (keys masked). | `admin:read` | No | No | | `cloudstack_test` | Test an endpoint with an API key and secret without saving; lists zones. | `infra:write` (+ `secrets:write`, because you pass the keys) | No | No | | `cloudstack_connect` | Save the CloudStack connection. | `infra:write` (+ `secrets:write`) | Yes | Always | | `proxmox_list` | Proxmox VE clusters added as sources, with health. | `admin:read` | No | No | | `proxmox_add` | Add a Proxmox VE cluster with an API token (`user@realm!tokenid`) and its secret. | `infra:write` (+ `secrets:write`) | Yes | No | | `proxmox_test` | Connect to a cluster by id; the first success pins its TLS certificate. | `infra:write` | Yes | No | | `proxmox_attach_zone` | Sell a Proxmox cluster in an existing zone, optionally with a node as region. | `infra:write` | Yes | No | | `zones_list` | Zones (locations) customers can choose. | `admin:read` | No | No | | `zone_create` | Create a zone. | `infra:write` | Yes | No | ### Catalog and plans | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `templates_list` | OS templates customers can install. | `admin:read` | No | No | | `templates_sync` | Import OS templates from CloudStack. | `catalog:write` | Yes | No | | `template_update` | Enable or hide a template, rename it, set the login user or order. | `catalog:write` | Yes | No | | `offerings_list` | CloudStack service offerings (CPU, memory, root disk) to build plans from. | `admin:read` | No | No | | `plans_list` | Plans with price per hour and per month, size, zone and how many servers use them. | `admin:read` | No | No | | `plan_create` | Create a plan. Price as `hourly_usd`. CloudStack plans need `cs_offering_id`; Proxmox plans need `backend_id`. | `catalog:write` | Yes | No | | `plan_update` | Rename, show or hide, enable or disable, or re-price a plan. Size changes on a plan in use need a copy. | `catalog:write` | Yes | When the price changes | | `plans_from_offerings` | Build a starter plan table from offerings (or Proxmox sizes) using your cost and a margin percent. Returns a preview; call again with `apply=true` to create the plans. | `catalog:write` | With `apply=true` | Always on apply | | `quota_defaults_get` | Default customer limits. | `admin:read` | No | No | | `quota_defaults_set` | Change `quota_*` settings. | `catalog:write` | Yes | No | | `coupons_list` | Discount and gift code batches. | `admin:read` | No | No | | `coupon_create` | Create a batch of codes: type `money` (face value in USD cents) or `percent` (1 to 100, with a maximum discount). | `catalog:write` | Yes | Always | ### Customers and resellers | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `customers_search` | Find customers by e-mail, name or IP. | `admin:read` | No | No | | `customer_get` | One customer's account, balance, servers and recent billing. | `admin:read` | No | No | | `customer_adjust_balance` | Add (positive) or remove (negative) credit in USD cents. A reason is required. | `billing:write` | Yes | Always | | `customer_block` | Block a customer, optionally stopping their servers. A reason is required. | `customers:write` | Yes | Always | | `customer_unblock` | Unblock a customer. | `customers:write` | Yes | No | | `resellers_list` | Resellers with wallet and status. | `admin:read` | No | No | | `reseller_get` | One reseller's site, wallet, users and gateways. | `admin:read` | No | No | ### AI provider | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `ai_provider_get` | The OpenAI-compatible provider used by the customer AI Chat and AI Agent (key masked). | `admin:read` | No | No | | `ai_provider_set` | Set the provider: base URL, model, API key, fallback models, reasoning effort, enabled. | `ai:write` (+ `secrets:write` for the key) | Yes | No | | `ai_provider_test` | Ask the saved provider one question to check it works. | `ai:write` | No | No | ### Configuration files | Tool | What it does | Scope | Changes data | Asks you | |---|---|---|---|---| | `config_export` | Export the configuration as YAML (secrets become `secret://` references). | `admin:read` | No | No | | `config_plan` | Compare a YAML configuration with the live panel and list what would change. | `admin:read` | No | No | | `config_apply` | Apply a YAML configuration section by section. Each change follows the normal scope and confirmation rules. | `admin:read` + the scope of every change | Yes | Per change | See [Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/) for the file format. ## Prompts The server also offers three ready-made prompts that clients can show as shortcuts: | Prompt | Argument | What it asks the AI to do | |---|---|---| | `initial-setup` | none | Call `setup_checklist`, explain each open step in one sentence, ask you for the information it needs, do the step with the matching tool, preview money or security changes, and finish with `system_health`. | | `add-proxmox` | `endpoint` (optional, `https://host:8006`) | Ask for the API token, then `proxmox_add`, `proxmox_test`, zone tools and `proxmox_attach_zone`; then propose plans with `plans_from_offerings` and wait for your approval. | | `launch-pricing` | `margin_pct` (optional, default 40) | Ask for your monthly cost per vCPU, per GB of RAM and per GB of disk, show a preview table with cost, price per month and price per hour, and create the plans after you approve. | ## Confirmation (`confirm_token`) Actions that move money, delete data, change payment credentials or sign-in security are two-step: 1. The first call changes nothing. The tool result starts with "CONFIRMATION REQUIRED" and includes a **preview** (what will be called, why it is sensitive, the new values and the current ones) and a `confirm_token`. 2. The client must show you the preview. Only after you approve does it call the same tool again with the same arguments plus `confirm_token`. 3. The token works once, only for that exact request, and expires after 5 minutes. The server instructions tell the AI to treat tool results as data, not as instructions, and never to repeat a call with a token you have not approved. You are the safeguard: read the preview. ::: warning Do not auto-approve Some clients can approve every tool call automatically. For write tools, keep per-call approval on, or use a read-only key, so a wrong plan from the AI cannot reach your panel unseen. ::: ## How it works | Item | Detail | |---|---| | Transport | `POST /mcp/admin` with a JSON-RPC 2.0 message; the answer is `application/json`. Up to 20 messages in a batch. `GET /mcp/admin` answers 405 (no server stream); `DELETE /mcp/admin` ends a session. | | Authentication | `Authorization: Bearer ak_...` (a manual key or an OAuth access token). Missing or wrong credentials give 401 with `WWW-Authenticate: Bearer ... resource_metadata=...`, which lets OAuth clients start sign-in by themselves. | | Sessions | `Mcp-Session-Id` is issued by `initialize`, bound to that key and valid 24 hours. | | Capabilities | Tools and prompts. No resources. | | Size | Request body up to 1 MB; a tool result is cut after about 100 KB. | | Origin | A request carrying an `Origin` header from another site is refused (`origin_not_allowed`). Command-line and desktop clients send none. | | Same gate as REST | Each tool call is re-sent through the panel's own request pipeline with your key, so scopes, IP ranges, rate limit, confirmation and the activity log apply exactly as for direct API calls. | ## Limits & notes - A tool you cannot see is a scope you did not grant. Add the scope to a new key. - Setting a password or API key through an AI client means the secret passes through that AI. If you prefer, leave `secrets:write` off and enter secrets yourself in the admin, or use the AI Chat **Secret** field ([AI Chat in the admin](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/#secrets)) or `secret://` references ([Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/#secrets)). - Clients do not share sessions: each connection starts with `initialize`. - Revoke the key in **API & AI agents** to cut an AI client off at once. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | 401 `invalid_key` | Wrong, truncated or non-admin key (for example a `vk_` key) | Use an `ak_` key. | | 401 `key_expired` or `key_revoked` | The key ran out or was revoked | Create a new key. | | 403 `ip_not_allowed` | Your IP is outside the key's IP ranges | Connect from an allowed IP. | | 403 `origin_not_allowed` | A browser page on another origin called the endpoint | Use a desktop or CLI client. | | 404 on a request with `Mcp-Session-Id` | The session belongs to another key or expired | Send `initialize` again. | | 400 unsupported `MCP-Protocol-Version` | The client uses a version the server does not know | Update the client; supported: 2025-11-25, 2025-06-18, 2025-03-26. | | The tool list is short | Key scopes | Create a key with more scopes. | | `insufficient_scope` on a call | A tool needs a second scope, for example `secrets:write` | Add the scope to a new key. | | 429 `rate_limited` | More calls per minute than the key allows | Wait for `Retry-After` or raise the limit on a new key. | | claude.ai or ChatGPT cannot connect | The URL is not reachable from the internet, or a proxy blocks `/mcp/admin` or `/oauth-admin/` | Check DNS, TLS and the reverse proxy. | For all error codes see [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#errors). --- # claude.ai and ChatGPT connectors > **Where in the admin:** System › Settings › API & AI agents (`/agent/keys`), tab **Connected apps**. The consent screen opens at `/agent/consent` when a connector asks for access (it is not in the menu). ## What it does Web AI apps cannot read a key from your disk. They connect with **OAuth**: you add the panel's MCP URL, the app sends you to your own admin to sign in, you choose what the app may do and for how long, and the app receives short-lived tokens. No key is copied or pasted. You can cut the app off at any time. Use this for claude.ai custom connectors, ChatGPT developer-mode connectors and any other MCP client that supports OAuth with dynamic client registration. Command-line tools can use the same flow, but an [admin API key](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/) is simpler for them. ## Requirements | Item | Requirement | |---|---| | URL | `https://cloud.example.com/mcp/admin`, reachable from the internet (the vendor's servers connect to it). | | Reverse proxy | If you put your own proxy in front of the panel, it must forward `/mcp/admin`, `/oauth-admin/` and the `/.well-known/` paths to the panel. | | You | Signed in to the NexCMP admin **in the same browser** you use for the consent screen. | | Client | Supports MCP over Streamable HTTP, OAuth 2.1 with PKCE (S256) and dynamic client registration. | ## Connect 1. In the AI app, add a custom MCP connector and paste `https://cloud.example.com/mcp/admin`. In claude.ai this is under the connectors settings (add custom connector); in ChatGPT it is under developer mode (add an MCP connector, authentication OAuth). 2. Choose **Sign in** (or "Connect") in the app. Your browser opens the NexCMP admin consent screen. If you are not signed in, sign in first; the screen continues afterwards. 3. Read the card: "**Example AI app** wants to manage this NexCMP admin" (the name of the app that is asking), with the address you will be sent back to. 4. Tick the scopes to grant. The ones the app asked for are pre-ticked; if it asked for none, only `admin:read` is ticked. See [Scopes](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#scopes). 5. Choose how long the connection lasts: **7**, **30** or **90** days (default 30). 6. Click **Allow**. The browser returns to the app, which is now connected. Click **Deny** to refuse. ![Consent screen asking to allow an AI app, with scope checkboxes, a duration choice and Allow and Deny buttons](https://nexcmp.com/docs/images/configure-with-ai/oauth-connectors-01-consent.webp) ::: important Choose scopes like you would for a key Grant the smallest set that does the job. Money, delete and security actions still stop at a preview and need your approval, but a connection with `settings:write` can still change your branding and email. ::: ## Manage and revoke Open **System › Settings › API & AI agents › Connected apps**. The table lists every app you connected: application, scopes, when it connected, when it expires and when it was last used. To cut an app off, click **Revoke** and confirm. Its tokens stop working immediately; the app has to go through the consent screen again to reconnect. ![Connected apps tab listing one AI app with its scopes, expiry and a Revoke button](https://nexcmp.com/docs/images/configure-with-ai/oauth-connectors-02-connected-apps.webp) ## How the flow works | Step | Detail | |---|---| | Discovery | The app reads `/.well-known/oauth-protected-resource/mcp/admin` and then `/.well-known/oauth-authorization-server/oauth-admin`. The issuer is `https://cloud.example.com/oauth-admin`. | | Registration | The app registers itself (`POST /oauth-admin/register`, RFC 7591). Only public clients are supported (no client secret). The redirect address must be `https`, or `http` on the same computer (loopback). | | Authorization | `GET /oauth-admin/authorize` with PKCE method `S256`. A `resource` parameter, when sent, must be this panel's `/mcp/admin` URL. The request is valid for 10 minutes and is tied to the browser that started it. | | Consent | You choose scopes and duration (above). The authorization code is valid 60 seconds and works once. | | Tokens | `POST /oauth-admin/token` returns an **access token** (an `ak_o...` key, valid 1 hour or until the connection ends, whichever is sooner) and a **refresh token** (`akr_...`). Every refresh returns a new refresh token; the old one stops working. The connection ends after the days you chose. | | Revocation | **Revoke** in Connected apps, or the app calls `POST /oauth-admin/revoke` (RFC 7009). | The access token behaves like an admin key with the scopes you granted: same scope rules, confirmation, rate limit and activity log. Its calls show up on the **Activity** tab. ## Verify 1. In the app, ask "run system_health" (or "what tools do you have?"). The app lists the tools your scopes allow ([the tool list](https://nexcmp.com/docs/configure-with-ai/mcp-admin/#tools)). 2. Open **Connected apps**: **Last used** moves. 3. Open **Activity**: the calls are listed with the app's name and `mcp`. ## Limits & notes - If the app asks again to sign in after an hour, that is normal when a client does not refresh; reconnect. - A refresh token that is used twice is treated as stolen: the whole connection is revoked. Reconnect from the app. - An authorization code used twice is rejected (`invalid_grant`) and the tokens already issued from it are revoked. - OAuth access never gives more than the scope rules allow: [what a key can never do](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#what-a-key-can-never-do) applies to connected apps too. - Vendors change their menus and what they support. If a vendor's app does not offer custom connectors on your plan, use an [admin API key](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/) with a desktop or command-line client instead. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Consent screen says "Wrong browser" | The request was started in another browser than the one you are signed in to | Start the connection again from the AI app in the same browser. | | Consent screen says the request expired or is missing | More than 10 minutes passed, or the link was opened twice | Start the connection again from the AI app. | | "Unknown client" or "redirect address does not match" page | The app registration is stale, or it uses a redirect address it did not register | Remove the connector in the app and add it again. | | `invalid_target` | The `resource` sent is not this panel's `/mcp/admin` URL | Use the exact URL shown on the **Connect an AI** tab. | | `invalid_request` about PKCE | The client does not send `code_challenge_method=S256` | Use a client that supports OAuth 2.1 with PKCE. | | The app connects but a call returns `insufficient_scope` | You did not tick that scope | Revoke the connection and connect again with the scope ticked. | | The app cannot reach the URL | DNS, TLS or the reverse proxy blocks `/mcp/admin`, `/oauth-admin/` or `/.well-known/` | Check the proxy rules. | --- # Configuration as code > **Where in the admin:** System › Settings › Import/Export (`/agent/config`). On the server: the commands `nexcmp config export`, `plan` and `apply`, and `nexcmp admin `. ## What it does Your panel configuration (branding, general settings, email, Telegram alerts, modules, currency, payment gateways, limits, zones, plans, the AI provider and the legal pages) can be saved as one YAML file. You can edit the file yourself, ask an AI assistant to edit it, keep it in version control, and apply it to the same or another panel. Before anything changes you always see a list of differences. Secrets are never written to the file. Use it to clone a setup to a second installation, to review changes before they go live, to restore settings after a mistake, or to let an AI propose a whole configuration that you read as one document. ## Requirements | Item | Requirement | |---|---| | NexCMP | 1.4.0 or newer. | | Command line | You are `root` on the panel server (use `sudo`). The command asks the panel for a temporary key (15 minutes, all scopes) by itself. To use a narrower key instead, set `NEXCMP_KEY` to an [admin API key](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/) and keep it through sudo, for example `sudo --preserve-env=NEXCMP_KEY nexcmp config plan -f nexcmp.yaml`. | | Admin page | Signed in to the admin. The page is read-only on a demo panel. | | Secrets | Values supplied at apply time (see [Secrets](#secrets)); never inside the file. | ## Export, edit, preview, apply 1. Export the current configuration: ```bash sudo nexcmp config export -o nexcmp.yaml ``` Without `-o` the YAML is printed to the screen. 2. Edit `nexcmp.yaml` (see the [example](#example-file) below). Keep `version: 1` on top. 3. Preview what would change. This changes nothing: ```bash sudo nexcmp config plan -f nexcmp.yaml ``` Each line shows section, item, field and `old → new`. Lines marked `$` belong to billing sections (see [Billing sections](#billing-sections)). If the panel already matches the file you see "No changes". 4. Apply. Without `--yes` the command is a **dry run** and only prints the same list: ```bash sudo nexcmp config apply -f nexcmp.yaml --yes ``` 5. Read the result: `applied`, `pending` (waiting for your confirmation), `skipped` and `errors`. | Option | Meaning | |---|---| | `-f ` | The YAML file (required for `plan` and `apply`). | | `--yes` | Really apply. Without it `apply` is a dry run. | | `--confirm-billing` | Also apply the billing sections (`payments`, `currency`, `plans`). Without it they are skipped. | | `--section ` | Apply only this section. Repeat the option for several. | | `-o ` | With `export`: write the YAML to a file. | The exit code is 0 when everything was applied or nothing changed, 1 when an error was returned, 2 for a usage or key error. ### Secrets {#secrets} The file holds references instead of secrets, such as `password: secret://smtp_password`. At apply time the value comes from an environment variable named `NEXCMP_SECRET_` plus the reference name in capitals: ```bash sudo -i read -rs NEXCMP_SECRET_SMTP_PASSWORD && export NEXCMP_SECRET_SMTP_PASSWORD read -rs NEXCMP_SECRET_STRIPE_SECRET_KEY && export NEXCMP_SECRET_STRIPE_SECRET_KEY nexcmp config apply -f nexcmp.yaml --yes --confirm-billing ``` If a reference has no value, the secret already saved in the panel is **kept**. A secret that was never saved and is not supplied stays empty. | Reference name | Where it goes | |---|---| | `smtp_password` | `email.smtp.password` | | `telegram_bot_token` | `telegram.bot_token` | | `stripe_secret_key`, `stripe_webhook_secret` | `payments.stripe` | | `paypal_client_secret` | `payments.paypal.client_secret` | | `binance_api_key`, `binance_api_secret` | `payments.crypto` | | `sepay_api_token`, `sepay_webhook_secret` | `payments.bank` | | `ai_provider_key` | `ai_provider.api_key` | ## Example file ```yaml # NexCMP configuration. Preview: nexcmp config plan -f nexcmp.yaml version: 1 branding: name: Example Cloud primary: "#1677ff" currency: usd seller_name: Example Cloud LLC settings: brand_name: Example Cloud support_email: support@example.com default_lang: en invoice_prefix: EX company_name: Example Cloud LLC company_address: "1 Example Street, New York, NY" min_topup_cents: 500 email: smtp: host: smtp.example.com port: 587 security: starttls username: mailer@example.com password: secret://smtp_password from_email: no-reply@example.com from_name: Example Cloud reply_to: support@example.com telegram: enabled: true chat_id: "123456789" bot_token: secret://telegram_bot_token modules: s3: true k8s: false currency: site_currency: usd manual_rate_vnd: 0 # 0 = automatic payments: stripe: mode: test publishable_key: pk_test_XXXX secret_key: secret://stripe_secret_key webhook_secret: secret://stripe_webhook_secret bank: bank_code: BANK account_number: "0000000000" account_name: EXAMPLE CLOUD LLC prefix: EX api_token: secret://sepay_api_token channels: pay_card_enabled: true quotas: quota_enabled: true quota_max_vms_24h: 5 quota_max_vms_30d: 30 zones: - slug: new-york name: New York country: US city: New York enabled: true is_default: true plans: - slug: vps-1c-1g name: 1 vCPU, 1 GB RAM, 25 GB cpu: 1 ram_mb: 1024 disk_gb: 25 hourly_usd: 0.0112 cs_offering_id: OFFERING_ID enabled: true visible: true ai_provider: enabled: true name: Example AI base_url: https://api.example.com/v1 model: example-model fallback_models: [example-model-mini] reasoning_effort: low api_key: secret://ai_provider_key legal: terms: en: | # Terms of service Your terms in simple markdown (no HTML). privacy: en: | # Privacy policy Your policy in simple markdown (no HTML). ``` A file may contain any subset of the sections. A section you leave out is not touched. A file larger than 1 MB, a missing `version: 1`, a different version number or an unknown section is rejected before anything runs. ## Sections | Section | What it controls | Notes | |---|---|---| | `branding` | Brand name (up to 60 characters), primary colour, display currency (`usd` or `vnd`), seller name. | Logos are not part of the file; use the [branding page](https://nexcmp.com/docs/customers/branding/). | | `settings` | A fixed list of general settings: `brand_name`, `support_email`, `support_telegram_url`, `default_lang`, `require_email_verified`, `min_topup_cents`, `max_topup_cents`, `invoice_prefix`, `company_name`, `company_address`, `company_tax_id`, `auto_destroy_days`, `low_balance_cents`. | Other keys are ignored. | | `email.smtp` | SMTP host, port, security (`starttls`, `tls`, `none`), user, password, sender name and address, reply-to. | [SMTP and e-mail templates](https://nexcmp.com/docs/administration/smtp-and-emails/). Changing host, port or security needs the password again. | | `telegram` | Alert channel on or off, chat id, bot token. | [Telegram notifications](https://nexcmp.com/docs/administration/telegram-notifications/). | | `modules` | Optional modules on or off (`key: true/false`). | Only keys the panel lets you switch. See [Modules](https://nexcmp.com/docs/infrastructure/modules/). | | `currency` | Display currency and a manual USD to VND rate (`0` automatic, otherwise 15000 to 50000). | Billing section. See [Currency and exchange rates](https://nexcmp.com/docs/catalog/currency-and-exchange-rates/). | | `payments` | `stripe`, `paypal`, `crypto`, `bank` credentials and `channels` switches (`pay_card_enabled`, `pay_wallets_enabled`, `pay_alipay_enabled`, `pay_paypal_enabled`, `pay_usdt_enabled`, `pay_sepay_enabled`). | Billing section. See [Payment channels](https://nexcmp.com/docs/billing/payment-channels/). | | `quotas` | `quota_*` limits. | See [Quotas](https://nexcmp.com/docs/billing/quotas/). | | `zones` | Zones by `slug` (required: `slug`, `name`, `country`; optional `city`, `enabled`, `is_default`). | New zones are created. A zone that already exists is **not changed** by the file. See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | `plans` | Plans by `slug` (required: `slug`, `name`, `cpu`, `ram_mb`, `disk_gb`, `hourly_usd`; optional `cs_offering_id`, `backend_id`, `zone_id`, `enabled`, `visible`, `sort`, `description`). | Billing section. New plans are created, existing ones are updated by slug. Prices are USD per hour. See [Plans](https://nexcmp.com/docs/catalog/plans/). | | `ai_provider` | The panel AI provider used by customer AI Chat and AI Agent. | [AI provider](https://nexcmp.com/docs/ai/ai-providers/). | | `legal` | Terms and privacy text per language (`en`, `vi`). | [Legal pages](https://nexcmp.com/docs/customers/legal/). | The order of application is the order above: infrastructure-like items before plans. ## Rules to remember - **Idempotent.** Applying the same file twice changes nothing the second time. `plan` shows "No changes". - **No deletions.** The file never removes a zone, plan or setting. Removing an entry from the file leaves the panel as it is. - **Normal safety rules still apply.** From the command line each change is sent through the same checks as an API change: scopes and IP rules. A change that needs confirmation (a site-wide switch, for example) is reported under `pending` with a preview instead of being applied, unless you pass `--confirm-billing`, which confirms them all. - **AI clients cannot unlock billing.** When `config_apply` is called by an MCP client, `confirm_billing` is ignored; money changes wait for you. - **Check before you apply.** The JSON Schema describes every field. Point your editor at it, or give it to an AI so it writes valid YAML: `https://cloud.example.com/v1/admin/agent/config/schema.json` (publicly readable). ### Billing sections {#billing-sections} `payments`, `currency` and `plans` change prices or money paths. `nexcmp config apply --yes` **skips** them unless you add `--confirm-billing`; they are then listed under `skipped` ("billing section - pass confirm_billing"). The flag is your confirmation: with it, every billing change in the file (and any other change that would ask for confirmation) is applied without a second prompt. Run `plan` first and read each line marked `$`. When the same file is applied by an AI (the `config_apply` tool in an MCP client or AI Chat) the flag is ignored, and money changes wait for your approval instead. ## Use the Import/Export page 1. Open **System › Settings › Import/Export**. 2. Under **Export** click **Get current configuration**, then **Download .yaml** to save `nexcmp.yaml`. Secrets appear as placeholders. 3. Under **Import** choose a file, drag it into the box, or paste the YAML. 4. Optional: open **Secrets that come with it (not in the file)** and add a name and value for each reference, for example `smtp_password`. 5. Click **Preview changes**. The table groups the changes by section; money-related sections carry a "Money-related" tag. Untick sections you do not want. 6. If money-related sections are listed, tick **Also apply money-related changes (prices, payment gateways, balances)**. Without the tick they are skipped. 7. Click **Apply selected**. The result shows what was applied, skipped or failed. A change that is returned as waiting for confirmation appears as a confirmation card; press **Confirm** on it. The **JSON Schema** button at the top right opens the schema. ![Import/Export page with the Export card and the Import card holding exported YAML](https://nexcmp.com/docs/images/configure-with-ai/config-as-code-01-import-export.webp) ![Preview table of changes grouped by section with a billing tag and an Apply selected button](https://nexcmp.com/docs/images/configure-with-ai/config-as-code-02-preview.webp) ## Run any tool from the command line `nexcmp admin` runs the same tools as the AI assistants ([the tool list](https://nexcmp.com/docs/configure-with-ai/mcp-admin/#tools)) and prints JSON: ```bash sudo nexcmp admin tools # name, scope and title of every tool sudo nexcmp admin system_health sudo nexcmp admin customers_search q=customer@example.com sudo nexcmp admin settings_update --json '{"values":{"support_email":"support@example.com"}}' ``` Arguments are `key=value` pairs (values are read as JSON when they parse, otherwise as text) or one `--json '{...}'` object. If the action needs confirmation, the command prints the preview and stops with exit code 3 and "Nothing changed". Read the preview, then run the same command again with `--yes` to apply it. Run as root the command gets a 15-minute key with all scopes, so the confirmation step is your safeguard; with `NEXCMP_KEY` the key's own scopes apply. ## Verify 1. `sudo nexcmp config plan -f nexcmp.yaml` right after an `apply --yes` prints "No changes". 2. In the admin, the pages you changed show the new values, and **Logs › Admin actions** lists the changes. 3. Applying the same file again changes nothing. ## Limits & notes - The file covers the settings above, not customers, servers, invoices or infrastructure sources (CloudStack and Proxmox connections). Connect those with [AI Chat](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/) or the admin pages. - Zones and plans are matched by `slug`. Do not rename a slug in the file: it would be created as a new item. - A plan that customers already use cannot change its size; create a new plan instead. - Keep exported files private even though they contain no secrets: they list your prices, addresses and account names. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | `missing version: 1` | The file does not start with `version: 1` | Add it. | | `unsupported version 2` | The file has another version number | This panel reads version 1 only. | | `unknown section 'x' (known: ...)` | A misspelled or unsupported top-level key | Use the sections above. | | `YAML error: ...` | Invalid YAML (indentation, tabs) | Fix the line the message names. | | `config is larger than 1 MB` | File too big | Split it and use `--section`. | | `no API key: set NEXCMP_KEY=ak_... or run as root on the server` | The command found no key | Run with `sudo`, or export `NEXCMP_KEY` and keep it through sudo. | | `panel not reachable` | The panel service is not running on the default port | Check `sudo nexcmp status`. | | Billing lines under `skipped` | Billing sections need `--confirm-billing` | Add the flag and review the `$` lines first. | | Items under `pending` | The change needs confirmation | Open **API & AI agents**, or the Import/Export result, and press **Confirm** on the card. | | A secret was not updated | No `NEXCMP_SECRET_` value was set, so the saved secret was kept | Export the variable and apply again. | | Entry under `errors` with HTTP 403 `insufficient_scope` | The key lacks the scope of that section | Use a key with the scope, or run as root. | | Entry under `errors` with HTTP 403 `demo_read_only` | Demo panel | Apply on a real installation. | --- # Example prompts > **Where in the admin:** AI Chat (`/agent`) or the floating **Ask AI** button on any admin page. The same prompts work in any AI client connected over [MCP](https://nexcmp.com/docs/configure-with-ai/mcp-admin/). ## What it does These prompts show how to ask. Each one lists what the assistant does behind the scenes, the scopes it needs (for an external client or a key) and where you are asked to approve. Replace the example values with your own. ::: tip Never type a secret into the chat text For passwords, API keys and tokens use the **Secret** field next to the AI Chat input ([details](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/#secrets)). The AI only sees a placeholder that stands for the secret, not the value. With an external AI client the secret passes through that client, so prefer entering it in the admin. ::: ## Check where you are ```text Check my setup progress and tell me the next three things to do, in order. ``` The assistant calls `setup_checklist` and `system_health` (scope `admin:read`) and answers from the real state. See [Setup progress checklist](https://nexcmp.com/docs/configure-with-ai/setup-checklist/). ## First setup ```text Help me finish the initial setup. Go through the checklist, explain each open step in one sentence, ask me for what you need, and show me every change before it is applied. ``` This is the same as the MCP prompt `initial-setup`. It needs `admin:read` plus the scopes of the steps you accept. Expect a confirmation card for each change in AI Chat. ## Add a Proxmox VE cluster ```text I want to sell servers from my Proxmox VE cluster at https://pve.example.com:8006. Add it, test the connection, create a zone called "New York" in the US, attach the cluster to it, and then propose starter plans. ``` What happens: 1. The assistant asks for the API token id (`user@realm!tokenid`) and its secret. Put the secret in the **Secret** field. Create a limited token first: [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). 2. It calls `proxmox_add`, `proxmox_test`, `zone_create` and `proxmox_attach_zone`. 3. It proposes plans with `plans_from_offerings` and waits for your approval (see the next example). Scopes: `infra:write`, `secrets:write`, `catalog:write`, `admin:read`. ## Create a plan table with a 40% margin ```text Suggest a starter plan table from my CloudStack offerings. My costs are $4 per vCPU per month, $2 per GB of RAM per month and $0.08 per GB of disk per month. Use a 40% margin, show me the table with cost, price per month and price per hour, and create the plans only after I approve. ``` The assistant calls `plans_from_offerings` with `margin_pct` 40 and your costs. Margin is the share of the selling price that is profit: at 40%, a plan that costs you $60 sells for $100. The first call only returns a preview. When you approve, it calls the tool again with `apply=true`, which creates the plans in one step and asks for one confirmation because prices are involved. For Proxmox it uses `backend_id` and a default size list (1, 2, 2 and 4 vCPU). Scopes: `catalog:write`, `admin:read`. See [Plans](https://nexcmp.com/docs/catalog/plans/). ![AI Chat answer showing a preview table of plans with cost, monthly price and hourly price](https://nexcmp.com/docs/images/configure-with-ai/example-prompts-01-plan-preview.webp) ## Configure email and send a test message ```text Configure outgoing email with smtp.example.com, port 587, STARTTLS, user "mailer@example.com", sender name "Example Cloud", sender address "no-reply@example.com". Use my Secret field for the password "smtp_password". Then send a test email to admin@example.com. ``` The assistant calls `smtp_get`, `smtp_configure` and `smtp_test` and tells you whether the message was accepted. Scopes: `settings:write`, `secrets:write`. See [SMTP and e-mail templates](https://nexcmp.com/docs/administration/smtp-and-emails/). ## Set up Stripe ```text Set up Stripe in test mode. My publishable key is pk_test_XXXX. Use the secret key and the webhook secret from my Secret field (names stripe_secret_key and stripe_webhook_secret). Then test the credentials and tell me which webhook URL I must add in Stripe. ``` `payment_configure` is always a two-step action: you see a preview with the secret values masked and approve it. Then `payment_test` checks the credentials with Stripe. Scopes: `billing:write`, `secrets:write`. Switch to live mode only after a test payment. See [Stripe](https://nexcmp.com/docs/billing/stripe/). ## Turn on Telegram alerts ```text Add my Telegram chat 123456789 for admin alerts. The bot token is in my Secret field as telegram_bot_token. Send a test message. ``` The assistant calls `telegram_configure` and `telegram_test`. Scopes: `settings:write`, `secrets:write`. See [Telegram notifications](https://nexcmp.com/docs/administration/telegram-notifications/). ## Find a customer and add credit ```text Find the customer with e-mail customer@example.com, show me their balance and servers, and add $10.00 of credit because of the outage on Monday. ``` The assistant calls `customers_search` and `customer_get` (read only), then `customer_adjust_balance` with `delta_cents` 1000 and a reason. This **always** needs your approval: the confirmation card says "changes a customer balance" and shows the amount and the reason that will go into the audit log. Scopes: `admin:read`, `billing:write`. ## Export my configuration ```text Export my whole configuration as YAML. Do not include secrets. Then tell me which settings differ from a typical new installation. ``` The assistant calls `config_export`. Secrets are replaced by `secret://` references. Save the result as `nexcmp.yaml` and keep it in version control. See [Configuration as code](https://nexcmp.com/docs/configure-with-ai/config-as-code/). Scope: `admin:read`. ## Write good prompts - Give the goal and the facts: names, prices, e-mail addresses. Do not paste secrets. - Ask for a preview first for anything that costs money or deletes data. - Ask the assistant to "read the current value before changing it". The built-in instructions already say so. - If an answer looks wrong, ask "which tool did you call and with what arguments?". The tool cards in AI Chat show both. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | The assistant says it has no tool for something | The scope for that tool is not granted, or the action is closed to AI | Check [Admin API keys](https://nexcmp.com/docs/configure-with-ai/admin-api-keys/#what-a-key-can-never-do), or do it in the admin. | | It asks you to paste a password | The model ignored the instructions | Refuse; use the Secret field or enter the value in the admin. | | Nothing changes after "do it" | Changes are waiting for your confirmation | Scroll to the confirmation card and press **Confirm**. | | Plans were created at a price you did not expect | The costs or margin in the prompt were wrong | Edit the plans on the Plans page or ask the assistant to re-price them; price changes ask for confirmation. | --- # Setup progress checklist > **Where in the admin:** Overview › Dashboard (`/`), the **Setup progress** card at the top. Empty lists on Plans, Customers, Zones, Payments and Infrastructure show the same help. ## What it does A new installation needs ten things before the first customer can order and pay. The **Setup progress** card checks them for you from the real configuration, shows how many are done, names the **next step** and links to the page where you do it. If you have an AI source connected, **Do it for me** opens the [AI assistant](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/) with a ready question for that step. The assistant (and any AI client over [MCP](https://nexcmp.com/docs/configure-with-ai/mcp-admin/)) can read the same list with the `setup_checklist` tool. ## The ten steps | # | Step | It counts as done when | Where you do it | |---|---|---|---| | 1 | License active | The license is activated for your domain | System › Admin › License & updates (`/license`). See [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/). | | 2 | Connect an infrastructure source | CloudStack is connected, or at least one Proxmox VE cluster is added | Infrastructure › Cloud provider › Providers (`/infra/providers`). See [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/) and [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). | | 3 | Create a zone | At least one zone exists | Infrastructure › Datacenter › Zone (`/infra/zones`). See [Zones](https://nexcmp.com/docs/infrastructure/zones/). | | 4 | OS templates | At least one template is enabled | Services › Virtual Machines › Plans & pricing, tab Templates (`/vm/catalog?tab=templates`). See [OS templates](https://nexcmp.com/docs/infrastructure/templates/). | | 5 | Create plans | At least one plan is enabled | Services › Virtual Machines › Plans & pricing (`/vm/catalog`). See [Plans and pricing](https://nexcmp.com/docs/catalog/plans/). | | 6 | Set up payments | Stripe, PayPal, crypto or bank transfer is configured | Finance › Payment channels (`/payments`). See [Payment channels](https://nexcmp.com/docs/billing/payment-channels/). | | 7 | Email (SMTP) | Outgoing email is configured | System › Settings › Email (`/settings/email`). See [SMTP and e-mail templates](https://nexcmp.com/docs/administration/smtp-and-emails/). | | 8 | Branding | A logo is uploaded, or the brand name is changed from the default | System › Settings › Brand (`/settings/branding`). See [Branding](https://nexcmp.com/docs/customers/branding/). | | 9 | Terms & privacy | At least one legal text is filled in | System › Settings › Legal (`/settings/legal`). See [Legal pages](https://nexcmp.com/docs/customers/legal/). | | 10 | Place a test order | At least one order exists | Place an order as a customer in the customer portal to check that everything works end to end; the card links to the Billing page (`/billing`). | The card shows `done/total` (for example 6/10), a progress bar, the next step with **Open** and, when an AI source is connected, **Do it for me**. Click a step to jump to its page. Next to a step a small link opens the matching documentation page; some steps also show a detail such as "2 zone(s)". ![Setup progress card on the Overview with three of ten steps done and the Open and Do it for me buttons](https://nexcmp.com/docs/images/configure-with-ai/setup-checklist-01-card.webp) ## Use it 1. Open the Overview. The card sits at the top. Use the arrow to collapse it; the panel remembers your choice in your browser. 2. Click **Open** to go to the page for the next step, or click **Do it for me**. 3. With **Do it for me** the assistant opens with a request such as "Suggest a starter plan table from my offerings with a 40% margin, show me the preview before creating it." Answer its questions, then review and **Confirm** each card. See [AI Chat in the admin](https://nexcmp.com/docs/configure-with-ai/ai-chat-in-admin/). 4. When all ten steps are done the card disappears. ### Empty lists When Plans, Customers, Zones, Payments or Infrastructure is empty, the page shows a **Next step** block with an explanation, a button for the action, **Do it for me** and a link to the documentation, so you never meet a blank table. ![Empty Plans page with a next-step explanation and the Do it for me button](https://nexcmp.com/docs/images/configure-with-ai/setup-checklist-02-empty.webp) ## Ask for it in your own words You do not need the card. In AI Chat or any connected AI client you can ask: ```text Check my setup progress and take me through the open steps, one at a time. ``` This is also the MCP prompt `initial-setup`. The assistant calls `setup_checklist`, explains why each open step matters, asks for the information it needs and uses the matching tool. Scopes needed: `admin:read` to read the list, plus the scope of the step you want done. See [Example prompts](https://nexcmp.com/docs/configure-with-ai/example-prompts/). ## Verify 1. After you finish a step the card's count goes up the next time the Overview loads. 2. In the assistant, `setup_checklist` returns the same numbers, with `done: true` on the finished step. ## Limits & notes - The checks look at what is saved, not whether it works. A configured gateway counts as done even if its test fails. Use the **Test** buttons on each page, and the `system_health` tool for problems. - The card is for administrators only. - **Do it for me** needs an AI source connected under System › Settings › AI accounts; without one only **Open** is shown. - The ten steps are the same for every installation; a step you do not plan to use (for example payments on a private cloud) still counts as open. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | The card is missing | All ten steps are done, or the card could not be loaded | Reload the Overview. Ask `setup_checklist` in AI Chat to see the state. | | A step stays open after you did it | The save did not complete, or the condition is not yet met (for example a template exists but is disabled) | Open the step's page and check the table above. | | Branding stays open after changing the logo | Branding counts when a logo exists or the brand name differs from the default "NexCMP" | Upload a logo or set your own name. | | **Do it for me** does nothing | No AI source connected, or the demo panel | Connect a source in AI accounts. | --- # SMTP and e-mail templates > **Where in the admin:** System › Settings › Email (`/settings/email`) — tabs **SMTP** and **Email templates**. Delivery log: Overview › Support › Sent emails (`/support/emails`). ## What it does NexCMP sends e-mail for sign-up verification, password reset, "VM ready" (with the initial password), invoices, low balance, renewals, top-ups, support replies and more. All of them go through **one SMTP account that you provide**. Messages and templates exist in 16 languages; the customer's language is used. ## Requirements | Item | Requirement | |---|---| | SMTP server | Host, port and credentials of any provider (your own server, Brevo, Amazon SES, Mailgun, Google Workspace …) | | Network | Outbound TCP from the NexCMP server to the SMTP host and port (587 STARTTLS, 465 TLS or 25) | | Sender domain | A domain you control, with **SPF and DKIM** published for the provider and a DMARC record | | Permission | The SMTP account must be allowed to send as the **From** address | ::: warning Deliverability E-mails such as "VM ready" contain passwords. If the sender domain's SPF/DKIM/DMARC does not match the SMTP provider, they land in spam. The page reminds you of this. ::: ## Prepare the e-mail provider 1. In your provider's panel add and verify your sending domain (for example `example.com`). 2. Publish the SPF, DKIM and DMARC DNS records the provider shows. 3. Create an SMTP user (or API-SMTP key) and note host, port, user name and password. ## Connect in NexCMP 1. Open **System › Settings › Email**, tab **SMTP**. 2. Fill the form: | Field | Example | Notes | |---|---|---| | Host | `smtp.example.net` | SMTP server | | Port | `587` | | | Security | STARTTLS (587) | Choose the mode your provider requires | | Username | `smtp-user` | | | Password | (hidden) | Write-only: the page shows only whether one is set | | From email | `no-reply@example.com` | Must be authorised at the provider | | From name | `Example Cloud` | Defaults to your brand name | | Reply-To (optional) | `support@example.com` | Where customer replies go | ![The SMTP tab of Settings, Email with host, port, security, user name, sender fields and the Send test button](https://nexcmp.com/docs/images/administration/smtp-and-emails-01-smtp.webp) 3. Click **Save**, then **Send test** and enter your own address. The settings are stored in an access-restricted file (`smtp.json`, mode 0600) and only the background worker `nexcmp-vmd` sends mail; the web service only queues it. ## Verify - The test message arrives in the inbox (check spam too). In the message source, confirm `dkim=pass` and `spf=pass`. - Open **Support › Sent emails**: each message has a status (queued, sent, failed) and the delivery result. Failed messages are retried up to 3 times. - Register a test customer in the portal and confirm the verification e-mail arrives. ## Edit e-mail templates Tab **Email templates** lists every template by key, with the languages it exists in (for example `verify_email`, `reset_password`, `vm_ready`, `invoice_paid`, `low_balance`, `topup_received`, `ticket_reply`, `scale_up` …). 1. Click a template and choose a language. 2. Edit subject and body. Placeholders are filled in when a message is queued. 3. Save. The change applies **immediately** to messages queued afterwards. ![The Email templates tab listing template keys and their languages](https://nexcmp.com/docs/images/administration/smtp-and-emails-02-templates.webp) ## Limits and notes - One SMTP account per installation. Resellers' customers receive e-mail through the same account unless the reseller is configured otherwise. - Provider sending limits apply; NexCMP does not throttle beyond retrying. - Without SMTP the panel works, but sign-up verification and password reset cannot be completed by customers. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | Test e-mail never arrives | Wrong credentials, port blocked, sender not authorised | Check `journalctl -u nexcmp-vmd` for the SMTP error; try port 587 with STARTTLS; verify the From address at the provider. | | Messages go to spam | SPF/DKIM/DMARC missing or misaligned | Publish the provider's records and send from that domain. | | "failed" in Sent emails | Provider rejected the message | Open the row for the provider's response. | | Password field looks empty after saving | By design (write-only) | The label shows "current: set" once a password is stored. | --- # Telegram and admin alerts > **Where in the admin:** System › Settings › Admin alerts (`/settings/alerts`). Support shortcuts: Customers › Support › Tickets (`/support/tickets`), tab **Settings**. ## What it does NexCMP notifies **you** — the operator — when something needs attention, and when a customer signs up or a deposit is credited. Alerts go to one or more **channels**: Telegram, a webhook, or e-mail. Events sent to the admin channels include: deployment failure, VM missing, network down, free IPs below 30, an expiring item (1-hour warning), a Stripe dispute, a dead worker, cluster RAM above its threshold, new customer sign-up, and credited deposits. Customer events are queued durably and retried with increasing delay (30 seconds up to 1 hour) until Telegram accepts them. ## Requirements | Item | Requirement | |---|---| | Telegram | A bot created with **@BotFather** (token) and the **chat ID** of the group or user that should receive messages | | Network | Outbound HTTPS to `api.telegram.org` | | Webhook (alternative) | An HTTPS endpoint that accepts the alert payload | ## Prepare Telegram 1. In Telegram open **@BotFather**, send `/newbot`, choose a name; copy the **bot token** (`123456789:AA...`). 2. Create a group, add the bot to it, and send any message. 3. Find the **chat ID**: open `https://api.telegram.org/bot/getUpdates` in a browser and read `chat.id` (group IDs start with `-`). ## Connect in NexCMP 1. Open **System › Settings › Admin alerts**. 2. In **Add channel** choose **Type: Telegram**, set **Destination** to the chat ID and **Secret (bot token)** to the token. Click **Add**. 3. Make sure the toggle **Send admin Telegram (VM module)** is on; the line *VM module status (read-only)* shows "on". ![The Admin alerts page with the Add channel form set to Telegram](https://nexcmp.com/docs/images/administration/telegram-notifications-01-channel.webp) ## Verify The channel appears under **Configured channels**. Trigger an event (for example register a test customer) and the message arrives in the group. If no channel is configured the page warns "Without a channel, attacks will go unnoticed" and, in the log, Telegram events are only recorded locally. ## Support shortcuts for customers The customer support widget can show **Telegram** and **Zalo** buttons. They are hidden until configured in the support settings: enter a `https://t.me/...` link for Telegram and the Zalo link or QR. Only `https://` links are accepted. The support chat can also notify your Telegram group with the question and a draft answer — see [AI Chat](https://nexcmp.com/docs/ai/ai-chat/). ## Limits and notes - The bot token is stored with the channel and never shown again. - Telegram events use the first enabled Telegram channel that has both a destination and a token. ## Troubleshooting | Symptom | Cause | Fix | |---|---|---| | No message arrives | Wrong chat ID, bot not in the group, or the bot was never messaged | Re-check the ID with `getUpdates`; send a message to the bot or group first. | | Works for a while then stops | Token revoked in BotFather | Create a new token and update the channel. | | `Telegram: no channel` in logs | No enabled Telegram channel | Add one as above. | --- # Audit log and activity > **Where in the admin:** System › Logs › Activity (`/logs`), Admin actions (`/logs/admin`), Console (`/logs/console`), Login (`/logs/logins`), VM events (`/logs/vm-events`); Overview › Support › Sent emails. NexCMP records every significant action. Use the **Activity** page as the single timeline, and the other tabs to narrow down. ## Tabs | Tab | Content | |---|---| | **Activity** | Unified timeline across all sources: admin actions, VM events, logins, affiliate and system errors | | **Admin actions** | What administrators changed (for example `customer.block`, `customer.delete`), with the administrator's IP | | **Console** | Admin console sign-ins and system requests such as an update request | | **Login** | Customer sign-ins and failures | | **VM events** | Deploy steps, create, password, destroy and other lifecycle events per virtual machine | ## Reading a row Columns: **Time**, **Source**, **Level**, **Actor** (who), **Target**, **Services**, **Action** (event code such as `customer.delete`) and **Details**. Click **Open** for the full record. ![The Activity page with source, site and service filters above the timeline](https://nexcmp.com/docs/images/administration/audit-log-01-activity.webp) ## Filter and search 1. Pick a **source**, **site** and **service** from the drop-downs. 2. Set a **From date** / **To date** range. 3. Type an event code (for example `customer.block` or `destroy`) in the search box — activity search matches the event code. ## Retention and exports Records live in the NexCMP databases in `/var/lib/nexcmp` and are included in [backups](https://nexcmp.com/docs/getting-started/backup-and-restore/). Retention depends on the log: customer sign-in attempts older than 180 days and customer API-key request logs older than 60 days are cleaned up. Admin API-key audit keeps up to about 200,000 recent records. These limits do not imply the same retention for the unified activity timeline. ## Common uses | Question | Where to look | |---|---| | Who suspended this customer? | Admin actions, search `customer.block` | | Why did a VM disappear? | VM events, filter by the VM's ID; also Activity | | Did the customer receive the e-mail? | Overview › Support › Sent emails | | When was the last update requested? | Console tab | --- # Security > **Where in the admin:** System › Admin › Console security (`/account`), Admin users (`/users`); System › Settings › Customer logins (`/settings/login`). ## Admin console ### Two-factor authentication (TOTP) The console is reachable from the Internet, so turn on TOTP. 1. Open **System › Admin › Console security**. 2. Under **Two-factor authentication (TOTP)** click **Start setup**. 3. Scan the QR code with Google Authenticator, Authy, 1Password or Bitwarden, then enter the 6-digit code. ### Allowed console IP ranges Under **Allowed console IP ranges** enter one IP or CIDR (IPv4 or IPv6) per line. **Empty means anywhere.** Outside the range both sign-in and the admin API are blocked. ::: warning Do not lock yourself out The page shows your current IP and requires that the new list includes it. Use **+ Add my IP**. If you are locked out anyway, use the [emergency access](https://nexcmp.com/docs/getting-started/cli-reference/#emergency-access) from the server. ::: ### Change password Fill **Current password**, **New password** and **Confirm new password**. Use a long, unique password (12 characters or more; the setup wizard enforces 12 for the first administrator). ### Administrator accounts and roles Open **System › Admin › Admin users** and use **+ Create account**. | Role | Access | |---|---| | `admin` | Full access to the admin API | | `staff` | Read-only across the whole system | | `user` | Read-only view of one customer's resources | The root account is the one created in the [setup wizard](https://nexcmp.com/docs/getting-started/setup-wizard/). Row actions: **Edit**, **Reset PW**, **Key**, **Delete**. Passwords are stored as salted hashes; changing or locking an account ends its sessions at once. ## Customer sign-in Open **System › Settings › Customer logins**. | Setting | What it does | |---|---| | **Google sign-in** | Enable, then enter the OAuth client ID and secret. Copy the **Callback URL** shown on the page into *Google Cloud Console › APIs & Services › Credentials › OAuth client ID › Authorized redirect URIs*. Use **Test configuration**. | | **GitHub login** | Same, with *GitHub › Settings › Developer settings › OAuth Apps › Authorization callback URL* | | **Passkey** | Fingerprint, PIN or hardware key login; on by default. Passkeys are bound to the domain; each reseller domain works on its own. Enter an old domain in *Legacy passkey Domain* after a rebrand so customers get a helpful hint | | **Reauthenticate when adding sign-in methods** | Ask for the password again before adding a passkey or linking Google/GitHub (off by default) | | **Captcha** | Cloudflare Turnstile or Google reCAPTCHA on sign-up: enable, choose the provider, enter the site key and secret key | | **Block e-mail domains** | One domain per line (max 2000). Matching addresses are rejected on sign-up with `email_domain_blocked` | ![The Customer logins page showing Google sign-in with its callback URL](https://nexcmp.com/docs/images/administration/security-01-customer-logins.webp) ### Built-in policies | Policy | Value | |---|---| | Customer password | At least 10 characters, letters and digits | | Customer session | 30 days; "login as customer" sessions last 1 hour | | E-mail verification | Required before creating a VM when *require e-mail verified* is on in the sales settings | | Anti-carding | Accounts with less than the minimum top-up can create only a limited number of VMs | ## Server hardening - Port 3000 is closed after setup and TLS; see [Installation](https://nexcmp.com/docs/getting-started/installation/#the-panel-port-3000). - The service runs as the unprivileged `nexcmp` user with a restricted systemd sandbox. - API keys, payment credentials and SMTP passwords are stored encrypted; the master key is in `/var/lib/nexcmp/secrets` — protect [backups](https://nexcmp.com/docs/getting-started/backup-and-restore/). - Releases are verified with SHA-256 and an Ed25519 signature before installation. --- # General settings > **Where in the admin:** System › Settings › General (`/settings`). Changes take effect immediately (30-second cache). The page has three sections. ## Branding & URLs | Field | Purpose | |---|---| | Brand name (e-mail, invoices) | Shown in e-mails and invoices. Logo and colours are in [Branding](https://nexcmp.com/docs/customers/branding/) | | Default language | Language for new visitors | | Customer app URL | Public address of the portal, for example `https://cloud.example.com` | | Console URL | Address of the VM console service | | Admin URL | Link used when you "log in as" a reseller's customer | | Partner console URL | Used in invitation links, reseller e-mails and the redirect after a top-up | The wizard fills these in from your domain; change them only if you move the portal. ## Seller & invoicing Printed on every invoice: | Field | Example | |---|---| | Legal entity name | Example Cloud Ltd | | Tax ID | `TAX-000000` | | Address | 1 Example Street, New York | | Contact e-mail | `billing@example.com` | | Invoice number prefix | `INV` (1–8 characters, A–Z and 0–9) | Existing invoices keep the seller details and number they were issued with; changes affect **new** invoices only. ## Maintenance & region Turn on **Customer app MAINTENANCE** to show a banner and **block all customer write actions** while the admin console keeps working. Enter the **Maintenance message** customers will see. Use this during upgrades of your infrastructure. ![The General settings page with brand name, default language and URL fields](https://nexcmp.com/docs/images/administration/general-settings-01-general.webp) ## Other settings pages | Page | Guide | |---|---| | Brand | [Branding](https://nexcmp.com/docs/customers/branding/) | | Legal | [Legal](https://nexcmp.com/docs/customers/legal/) | | Customer logins | [Security](https://nexcmp.com/docs/administration/security/) | | Email | [SMTP and e-mail templates](https://nexcmp.com/docs/administration/smtp-and-emails/) | | Admin alerts | [Telegram and admin alerts](https://nexcmp.com/docs/administration/telegram-notifications/) | | AI provider | [AI providers](https://nexcmp.com/docs/ai/ai-providers/) | --- # Report a bug > **Where in the admin:** Top bar › **Report a bug** (next to Docs; shown to the main administrator only). Found a defect in NexCMP? Press **Report a bug**, describe the problem and send it. The report reaches the NexCMP team as a ticket on your license account in one step — you do not sign in anywhere else, and you do not copy log files by hand. Every plan can use it: NexCMP Free covers bug fixes, and so does NexCMP Source. ::: info Bug reports are not support requests NexCMP Free has no technical support, so use this form for defects in the software. Setup help and migrations are part of [NexCMP Source](https://nexcmp.com/pricing). ::: ## Requirements | Item | What you need | |---|---| | Version | NexCMP 1.5.2 or later | | Who can send | The main administrator account. Staff accounts (read-only), reseller consoles, customers and the read-only demo cannot send | | License | An activated license (Free or Source) that is not suspended | | Network | The server must reach `https://nexcmp.com` over HTTPS (the same address it uses for license checks and updates) | ## Send a report 1. Click **Report a bug** in the top bar. On a phone, open the menu and choose **Report a bug**. 2. Enter a short **Subject**. 3. Under **What happened**, describe what you did, what you saw and what you expected. 4. Optionally list the **Steps to reproduce**. 5. Choose the **Impact**: **Blocking** (you cannot work), **Major** (a main feature is broken) or **Minor** (a small or cosmetic problem). 6. Leave **Attach diagnostics** on (recommended) and read the **What will be sent** box. 7. Click **Send report**. In 1.6.1 the form body scrolls while the action buttons stay visible, including on a phone. Password values following `password:` or `password=` are masked without removing the rest of the report. Review your text before sending and omit customer data and credentials. ![The Report a bug dialog with the title, description, impact and the list of what will be sent](https://nexcmp.com/docs/images/administration/report-a-bug-01-dialog.webp) When the report is accepted, the dialog shows the ticket number and a link. Replies, if the team sends one, go to the email address of your license account at nexcmp.com, and the ticket is also listed under **Support** when you sign in there as the license owner. ## What is sent The **What will be sent** box shows exactly this before you click Send: | Sent | Details | |---|---| | Your text | Title, description, steps and impact | | Version, plan, domain | Your NexCMP version, Free or Source, and the domain of the installation | | Current page | The page you are on in the admin, without anything after `?` or `#` | | Browser | Your browser's user-agent text (name, version and operating system), cut to 200 characters | | Recent errors | Up to the last 5 errors in this browser tab. A script error is the message only; for a failed request only the method, the path and the status code | You can switch off **Attach diagnostics**; then only your own text and the version, plan and domain are sent. ## What is never sent - Passwords, tokens, cookies and API keys. Anything that looks like one in your text or in an error message is masked (shown as `***` or `[redacted]`) on your server before anything is sent. - The content of requests and responses — failed requests are recorded as method, path and status only. - Your customers' data (names, emails, orders, invoices) and their IP addresses. - Your browser's IP address and any file from your server. As with every license check, the license server sees the IP address your server connects from. The ticket is tied to your installation by the license token the server already holds, so the version, domain and installation are set by the server and cannot be changed from the browser. ## Limits | Limit | Value | |---|---| | Subject | 150 characters | | Description plus steps | 8 KB together | | Reports | 5 per hour and 20 per day for the whole installation | Sending the same report twice (for example after a network error) does not create a second ticket. ## Troubleshooting | Message | Cause | Fix | |---|---|---| | Activate your license first | The installation has no activated license | Open **License & updates** and activate, then send again | | Could not reach the license server | The server cannot reach `nexcmp.com` | Check outbound HTTPS and DNS from the server, then press **Send report** again — your text is kept | | Too many bug reports from this installation | You reached 5 per hour or 20 per day | Wait and try again; add new findings to the open ticket instead | | This license is suspended | The license is suspended or refunded | See [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/) | | This installation was revoked | The installation was revoked in the license portal | Activate it again from **License & updates** | | The license token could not be refreshed | The server could not renew its license token | Open **License & updates**, press **Refresh**, then send again | | No Report a bug button | You are signed in with a staff, reseller or customer account, or on the read-only demo | Sign in as the main administrator | ::: tip For the AI assistant and API keys The assistant in the admin and admin API keys cannot send bug reports on your behalf: the button needs a person signed in as the main administrator. ::: --- # Installation errors The installer stops at the first fatal problem and prints a red line starting with `✗`. Fix the cause and **run the same command again** — the installer is safe to repeat. Informational lines start with `•`, successful steps with `✓`. ## Machine checks | Message | Cause | Fix | |---|---|---| | `must be run as root (sudo)` | Not running as root | Prefix the command with `sudo`, or run as root. | | `only Ubuntu 22.04/24.04 is supported (this host: …)` | Another distribution or version | Use an Ubuntu 22.04 or 24.04 server. | | `only x86_64 is supported` | ARM or other CPU | Use an x86_64 server. | | `requires ≥ 2 GB RAM (host has … MB)` | Too little memory | Add RAM. | | `requires ≥ 8 GB free disk space on / (… GB available)` | Root filesystem nearly full | Free space or enlarge `/`. | | `missing --license` / `missing --domain` | First install without those parameters | Pass both — see [Installation](https://nexcmp.com/docs/getting-started/installation/#parameters). | | `unknown argument: …` | Typo in a parameter | Check spelling; run with `--help`. | | `apt-get failed (another apt/dpkg process, e.g. unattended-upgrades, may still be running)` | The package manager is locked | Wait a few minutes (a fresh Ubuntu runs automatic updates after boot) and re-run. | | `… is a symlink — aborting` | `/opt/nexcmp` or `/etc/nexcmp` is a symbolic link | Replace it with a real directory — the installer refuses symlinks for security. | ## License activation | Message | Cause | Fix | |---|---|---| | `cannot connect to https://nexcmp.com` | No outbound HTTPS to the license server | Check DNS, firewall and proxy on the server: `curl -I https://nexcmp.com`. | | `license activation failed (HTTP …): ` | The license server rejected the key | Read the message: wrong key, domain not matching the license, license suspended or revoked. Check the license page at [nexcmp.com/licenses](https://nexcmp.com/licenses). | | `no release available for this license yet` | The license has no downloadable release | Contact sales; see [Support](https://nexcmp.com/docs/troubleshooting/support/). | ## Download and verification | Message | Cause | Fix | |---|---|---| | `failed to download release X` | Network problem during download | Re-run. | | `sha256 mismatch (… ≠ …)` | The file arrived damaged or was altered | Re-run; if it repeats, a proxy may be rewriting downloads — try from another network. | | `release X is unsigned — refusing to install` | The server delivered no signature | Never bypass; contact support. | | `INVALID SIGNATURE for version X — aborting` | The Ed25519 signature does not verify | Do **not** install. Contact support — this means the file is not an official release. | | `openssl failed to read public key` | Very old or broken OpenSSL | Update the `openssl` package. | | `invalid version: …` | Unexpected data from the server | Re-run; contact support if it persists. | ## Service start | Message | Cause | Fix | |---|---|---| | `service failed to start — check: journalctl -u nexcmp -n 50` | The panel did not answer `/healthz` within 3 minutes | Read the journal. Common causes: the port (default 3000) is already used (`ss -ltnp | grep 3000`) — re-install with `--port`; files owned by root in `/var/lib/nexcmp` — run `sudo nexcmp fix-owner`. | ## nginx and TLS | Message | Cause | Fix | |---|---|---| | `DNS does not point to (nor to Cloudflare) — skipping nginx/TLS` | The A/AAAA record is missing or wrong | See [DNS and Cloudflare](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/), then re-run. | | `certbot could not get a certificate yet — re-run the installer once DNS has settled` | Let's Encrypt validation failed (port 80 blocked, DNS not propagated, rate limit) | Open port 80, wait for DNS, re-run. Check `/var/log/letsencrypt/letsencrypt.log`. | | `certbot could not renew now — keeping the existing certificate` | Temporary renewal failure | Harmless; re-run later. | | `nginx rejected the HTTPS configuration — kept the previous one (check: nginx -t)` | Another nginx site conflicts (same `server_name`, duplicate settings) | Run `sudo nginx -t` and fix the conflict, then re-run. | | `apt-get could not install nginx` / `certbot`, or `E: Failed to fetch … 404 Not Found` | Package manager locked, or the server's package index is stale (typical on a template image that has not been updated for a while) | Run `sudo apt-get update`, wait for any other apt process to finish, then re-run the installer. | | `openssl could not create the self-signed certificate` | OpenSSL problem | Update `openssl`; re-run. | ## After installation | Symptom | Cause | Fix | |---|---|---| | Browser cannot open `https://` | DNS or TLS not ready | `sudo nexcmp status`; use [emergency access](https://nexcmp.com/docs/getting-started/cli-reference/#emergency-access). | | `ERR_TOO_MANY_REDIRECTS` | Cloudflare SSL mode **Flexible** | Set **Full (strict)** — [details](https://nexcmp.com/docs/getting-started/dns-and-cloudflare/). | | Wizard says "setup code" is wrong | Typo or a code from an earlier install | Run `sudo nexcmp status` and copy the current code. | | License shows HTTP 421 | Files in `/var/lib/nexcmp` owned by root | `sudo nexcmp fix-owner`, then `sudo systemctl restart nexcmp`. | --- # Update errors > **Where in the admin:** System › Admin › License & updates (`/license`) Updates are described in [Updating NexCMP](https://nexcmp.com/docs/getting-started/updating/). When the Update card or `sudo nexcmp update` reports an error: | Message | Cause | Fix | |---|---|---| | `update plan expired — renew on the license page to receive this feature release` | Your "updates until" date is before the release date. Security fixes are still delivered; feature releases are not | Renew at [nexcmp.com/licenses](https://nexcmp.com/licenses) ($99 per year for NexCMP Free), then check again. The software keeps running meanwhile. | | `sha256 mismatch (downloaded …, expected …)` | Damaged or altered download | Retry. If it repeats, check proxies on the path. | | `downgrade/same version rejected: X is not newer than Y` | The selected version is not newer than the running one | Nothing to do — you are up to date. | | `extraction failed / main binary missing` | Damaged archive or full disk | Check `df -h`; retry. | | `systemctl restart failed` | The service could not restart after replacing the binary | `journalctl -u nexcmp -n 80`; the previous binary is kept as `/opt/nexcmp/bin/nexcmp.bak` — see roll back below. | | Server error contacting the update server | No route to `nexcmp.com` or license token expired | Check outbound HTTPS; click **Check now** on the License card. | ## Roll back If a new version misbehaves: ```bash sudo systemctl stop nexcmp-vmd nexcmp sudo cp /opt/nexcmp/bin/nexcmp.bak /opt/nexcmp/bin/nexcmp echo | sudo tee /opt/nexcmp/VERSION sudo systemctl start nexcmp nexcmp-vmd ``` If a database migration already ran, restore your [backup](https://nexcmp.com/docs/getting-started/backup-and-restore/) taken before the update as well. ## `sudo nexcmp update` says it cannot find the installer `nexcmp update` runs the installer that ships inside the installed release (`/opt/nexcmp/releases//share/install.sh`). If that file is missing, run the install command from your license page again. --- # FAQ ## Licensing **What happens if my license expires or the server loses Internet access?** The token stays valid for 14 days and renews daily. After that, customers cannot create **new** services; running services, billing and logins keep working. See [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/). **Do I still get security fixes after my update entitlement ends?** Yes — security releases are always delivered; feature releases need an active entitlement. **Can I run NexCMP on a second server or domain?** Activate it with the key and the domain registered on the license — see [License and purchase](https://nexcmp.com/docs/getting-started/license-and-purchase/#moving-to-a-new-server-or-domain). ## Installation **Does NexCMP need Docker, PostgreSQL or Redis?** No. It is one program with SQLite databases, run by systemd as an unprivileged user. See [Requirements](https://nexcmp.com/docs/getting-started/requirements/). **Can I install without a public domain?** Yes, use `--no-nginx` and an SSH tunnel — see [Installation](https://nexcmp.com/docs/getting-started/installation/#installing-without-a-public-domain-lab-or-test). **Why is port 3000 closed after setup?** For security the panel listens on `127.0.0.1` once the wizard is done and TLS is active. Use `sudo nexcmp open-port` for emergencies. ## Infrastructure **Can I use CloudStack and Proxmox VE together?** Yes. A zone can have both; each plan chooses its source. See [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/) and [Zones](https://nexcmp.com/docs/infrastructure/zones/). **Do I need the ROOT API key of CloudStack?** No — and you should not use it. Create a dedicated account — [CloudStack](https://nexcmp.com/docs/infrastructure/cloudstack/). **Why do some tabs or buttons not appear on a virtual machine?** Pages show only what the VM's source supports (for example, Proxmox VE has no port forwarding). See the capability table in [Proxmox VE](https://nexcmp.com/docs/infrastructure/proxmox/). ## Operations **How do I move NexCMP to another server?** Back up, install the same version on the new server with the same license, restore — see [Backup and restore](https://nexcmp.com/docs/getting-started/backup-and-restore/). **Where are the logs?** `journalctl -u nexcmp -u nexcmp-vmd`, `/var/log/nexcmp`, and the admin console under System › Logs. See [Audit log](https://nexcmp.com/docs/administration/audit-log/). **Can I see the customer portal before I install?** Yes. The service pages on [nexcmp.com](https://nexcmp.com/platform/) show the customer portal tab by tab with sample data; open *View demo* for the full-screen viewer. To see a real install, write to sales. --- # Support ## Contact There is no contact form. Send an e-mail to **sales@nexcmp.com** — it covers licensing, renewals, domains and technical questions. Your license page at [nexcmp.com/licenses](https://nexcmp.com/licenses) also links to the same address. ## Before you write 1. Search these docs (press `/`) and check [Installation errors](https://nexcmp.com/docs/troubleshooting/installation-errors/) and [Update errors](https://nexcmp.com/docs/troubleshooting/update-errors/). 2. Collect the facts below — they let support answer in one reply. ## What to include | Item | How to get it | |---|---| | NexCMP version | `sudo nexcmp status` or System › Admin › License & updates | | Main domain and the last 4 characters of the license key | License & updates (never send the full key) | | What you did and what you expected | Exact steps | | The error text or a screenshot | Hide secrets before sending | | Service status | `sudo nexcmp status` | | Recent log lines | `sudo journalctl -u nexcmp -u nexcmp-vmd -n 100 --no-pager` | | Infrastructure | CloudStack or Proxmox VE version, one zone or several | ::: warning Never send secrets Do not send license keys, API keys, passwords, the setup code, payment credentials or backup archives. Mask them in logs and screenshots. ::: ## Security reports If you believe you found a vulnerability, e-mail the same address with "Security" in the subject and avoid posting details publicly until it is fixed.