Skip to content

Releases: zsoftly/zcp-cli

v0.0.23

Choose a tag to compare

@github-actions github-actions released this 08 Jul 17:31
30411c2

zcp v0.0.23 Release Notes

instance --wait now reports the VM's real state

zcp instance create --wait (and start --wait / stop --wait) previously
polled the CMP's cached list/show endpoint, which can keep reporting Starting
for many minutes after a VM is actually Running. On affected deployments
--wait could hang until it timed out even though the VM was already up.

--wait now polls the live GET /virtual-machines/{slug}/meta endpoint, which
performs a real-time reconcile against the underlying platform (CloudStack/APC)
and returns the authoritative state. Verified live end to end: with --wait,
create returned Running from /meta while the plain instance list still
showed Starting.

This is a client-side workaround for a CMP background state-sync issue (the
platform's own reconciliation is unreliable; state only refreshes on demand).
The on-demand /meta sync is authoritative, so the CLI polls it.

instance delete --delete-public-ip help corrected — the flag is currently a no-op

The flag advertised that deleting a VM releases its auto-assigned public IP, but
that never worked against the live API: the DELETE endpoint ignores it, and
the IP-releasing PUT .../destroy endpoint currently rejects API-token auth (a
CMP bug, reported and under fix). Until that lands, the help text, confirmation
prompt, and command examples now state plainly that the IP is not released
automatically, and that you must free it manually:

zcp instance delete my-vm --yes
zcp ip release <ip-slug> --yes

No behavior change — this corrects misleading messaging only. The real fix
(routing instance delete through PUT .../destroy) is implemented and
verified at the request level, and is held until the API accepts token auth.


Installation and upgrade

The install script installs the latest release and upgrades an existing
installation in place.

Linux / macOS

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Windows (PowerShell)

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Manual download: grab your platform's binary from the
Releases page, chmod +x, and
place it on your PATH.

Verify:

zcp version   # zcp version v0.0.23

First-time setup after installing:

zcp profile add default --region yul-1 --project default-9   # prompts for bearer token
zcp auth validate

Fixed

instance --wait reflects the real state

# Create and wait: returns when the VM is actually Running (polled via /meta),
# even while `instance list` still reports Starting.
zcp instance create --name my-vm --project default-9 --region yul-1 \
  --template ubuntu-2604-lts-1 --plan ca2m --billing-cycle hourly \
  --network-plan pnet-yul --storage-category premium-ssd --wait

SDK consumers get a new instance.Service.Meta(ctx, slug) method that returns
the live, hypervisor-synced view (authoritative state).

instance delete --delete-public-ip messaging

# The auto-assigned public IP is NOT released automatically yet (known CMP API
# bug, under fix). Free it manually after deleting:
zcp instance delete my-vm --yes && zcp ip release <ip-slug> --yes

v0.0.22

Choose a tag to compare

@github-actions github-actions released this 07 Jul 01:45

zcp v0.0.22 Release Notes

DNS records now display and delete correctly

The live DNS backend (PowerDNS) models records as record sets addressed by
name and type. PowerDNS exposes no record IDs and returns values in a
contents array. The CLI previously decoded neither, so on PowerDNS-backed
deployments record tables printed blank ID and CONTENT columns, and
dns record-delete demanded a numeric --record-id those deployments never
expose. Record deletion was impossible there. Backends that do expose record
IDs keep the legacy --record-id path.

This release aligns the CLI with how the backend actually works, verified live
end to end (create → show → delete → confirm gone).

Highlights:

  • Record content is visible again. zcp dns show and record-create
    tables show real values (multi-value sets joined, e.g.
    ns1.zsoftly.ca., ns2.zsoftly.ca.), and the dead ID column is gone.
  • dns record-delete works, by name and type.
  • Record names are relative. The backend appends the zone; the help text
    now says so (passing an FQDN used to silently create
    www.example.com.example.com.).
  • egress create retries its lookup and reports honestly when the backend
    silently drops an accepted rule (a platform-side issue found while testing).
  • docs/commands.md is now machine-validated: all 264 examples checked
    against the built CLI. Six sections documented commands that did not exist
    and are rewritten to the real trees.
  • L2 instances work, and instance create examples run as pasted thanks
    to first-time contributor @cokerrd: a new --is-public flag unblocks
    --network-type L2, and the required --network-plan/--storage-category
    flags are now in the examples and validated client-side.

Installation and upgrade

The install script installs the latest release and upgrades an existing
installation in place.

Linux / macOS

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Windows (PowerShell)

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Manual download: grab your platform's binary from the
Releases page, chmod +x, and
place it on your PATH.

Verify:

zcp version   # zcp version v0.0.22

First-time setup after installing:

zcp profile add default --region yul-1 --project default-9   # prompts for bearer token
zcp auth validate

Fixed

DNS record display and deletion

# Records show their content; sets are addressed by NAME + TYPE (no IDs)
zcp dns show example-com
# NAME               TYPE  CONTENT                           TTL
# www.example.com.   A     192.0.2.50                        3600
# example.com.       NS    ns1.zsoftly.ca., ns2.zsoftly.ca.  3600

# Create with a RELATIVE name (the backend appends the zone)
zcp dns record-create --domain example-com --name www --type A --content 192.0.2.50

# Delete by name and type (relative or fully qualified both work)
zcp dns record-delete --domain example-com --name www --type A

The legacy --record-id flag remains for deployments whose DNS backend exposes
record IDs. SDK consumers get DeleteRecordByName, CanonicalRecordFQDN, and
Record.Contents; the ID-based DeleteRecord is deprecated.

Egress rule creation reporting

The create endpoint returns no body, so the CLI resolves the new rule from the
rule list. It now retries that lookup (3 attempts over ~4s) before giving up,
and when the rule never appears (the API can return 200 yet create nothing on
some networks), the error says the backend may have dropped the rule, pointing
at the platform rather than the CLI.

L2 instance creation and complete create examples (community)

Contributed by @cokerrd, our first outside contributor. Two fixes to
instance create, both verified against the live API:

# L2 networks cannot carry a public IP. The new --is-public flag (default:
# true) unblocks them; the CLI rejects the invalid combination client-side.
zcp instance create --name my-l2-vm --template ubuntu-2604-lts-1 --plan ca2sl \
  --billing-cycle hourly --network-plan l2net-yul --network-type L2 \
  --storage-category premium-ssd --is-public=false \
  --region yul-1 --project default-9

# --network-plan and --storage-category are required by the API and are now
# in every example, marked required in help, and validated client-side.
zcp instance create --name my-vm --template ubuntu-2604-lts-1 --plan ca2sl \
  --billing-cycle hourly --network-plan pnet-yul --storage-category premium-ssd \
  --region yul-1 --project default-9

Command reference corrected and machine-validated

Six sections of docs/commands.md documented commands that do not exist
(monitoring create, vpn create --vpc, support close, dashboard status,
among others) or missed required flags (ip allocate without --plan/
--billing-cycle). All are rewritten to the real command trees, including
the previously undocumented kubernetes scale/get-config/upgrade-version/delete
and loadbalancer attach-vm/detach-vm/delete-rule. Every example in the
reference is now validated automatically against the CLI (command paths and
flags; 264 examples).


New Contributors

  • @cokerrd made their first contributions in
    #25 and
    #27, fixing L2 instance
    creation and the instance create examples. Both fixes were verified
    against the live platform before merge. Welcome, and thank you!

v0.0.21

Choose a tag to compare

@github-actions github-actions released this 05 Jul 13:45
cdd3ccf

zcp v0.0.21 Release Notes

Profile defaults now work everywhere, including create commands

zcp profile add default --region yul-1 --project default-9 stores your default scope
so you never repeat --region/--project. Until now that promise only held for
list/get commands: create and mutate commands (e.g. network create, instance create)
resolved their own scope from flags and environment variables only, so a fully
configured user still hit --region is required. That gap is closed: configure once,
and scoped commands pick the defaults up.

Highlights:

  • Profile default region/project are honored by create/mutate commands. The root
    scope gate now injects the resolved scope (flag > env > profile default, respecting
    --profile) onto the command's flags. Verified end-to-end against the production API.
  • First-run setup points at the production defaults. The installers print
    copy-paste setup for zcp profile add default --region yul-1 --project default-9;
    every account's initial project is default-9 (like us-east-1 on AWS).
  • All command examples use slugs verified against the live production catalog.
    Broken template, backup, and virtual-router plan slugs are fixed.

Fixed

Profile default region/project honored by create/mutate commands

zcp profile add default --region yul-1 --project default-9
zcp auth validate

# Previously: Error: --region is required
# Now: creates the network in yul-1 / default-9 from your profile defaults
zcp network create --name my-net --network-plan inet-yul --billing-cycle hourly

Explicit --region/--project flags and ZCP_REGION/ZCP_PROJECT still take
precedence over the profile default, and --profile <name> selects which profile's
defaults apply. Two command groups manage their own scope by design and are
unaffected: dns create (fixed default region; still needs an explicit
--project) and object-storage create/list (object-storage os-* regions).

First-run examples point at the production defaults

The Unix and Windows installers now end with copy-paste setup commands
(zcp profile add default --region yul-1 --project default-9, zcp auth validate)
plus matching ZCP_REGION/ZCP_PROJECT examples for scripts. README,
configuration docs, and command examples consistently use yul-1 as the primary
compute region and YUL-compatible plan slugs.

Command examples verified against the live production catalog

Examples that referenced nonexistent slugs are fixed:

Was Now Where
ubuntu-2604-lts ubuntu-2604-lts-1 instance/autoscale --template (template slugs are region-specific; this is yul-1's)
backup-1, backup-basic backup-yul backup create, vm-backup create --plan (backup plans are now enabled in the catalog)
virtual-private-cloud-vpc virtual-private-cloud-vpc-1 virtual-router create --plan

The docs Backup section was rewritten to show the real backup create flags
(--volume/--interval/--plan …) and to drop nonexistent backup get/backup restore subcommands. Verified live in yul-1: ca2sl/ca2sm/ca2sxs, b2g1,
pro-nvme, inet-yul, l2net-yul, k8s-la-yul-1, k8s-xla-yul-1,
vm-snapshot-yul, ipv4-yul, lb-yul, backup-yul, virtual-private-cloud-vpc-1,
ubuntu-2604-lts-1.

Upgrade notes

No breaking changes. If you have profile defaults configured (zcp profile add
with --region/--project), create/mutate commands that previously errored
without explicit flags now use those defaults automatically; pass --region/
--project (or set ZCP_REGION/ZCP_PROJECT) to override per invocation.


Installation

Linux / macOS / WSL (one-liner)

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Installs zcp to /usr/local/bin (you may be prompted for sudo). Set INSTALL_DIR to
choose another location, e.g. INSTALL_DIR="$HOME/.local/bin".

Windows (PowerShell)

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Installs zcp.exe to %LOCALAPPDATA%\Programs\zcp.

Manual download

Grab the binary for your platform from the
Releases page, make it executable, and put it on
your PATH.

OS Arch Asset
Linux x86_64 zcp-linux-amd64
Linux ARM64 zcp-linux-arm64
macOS Intel zcp-darwin-amd64
macOS Apple Silicon zcp-darwin-arm64
Windows x86_64 zcp-windows-amd64.exe
Windows ARM64 zcp-windows-arm64.exe
# Linux amd64 example
curl -Lo zcp https://github.com/zsoftly/zcp-cli/releases/latest/download/zcp-linux-amd64
chmod +x zcp
sudo mv zcp /usr/local/bin/zcp
# Windows amd64 example (PowerShell)
irm https://github.com/zsoftly/zcp-cli/releases/latest/download/zcp-windows-amd64.exe -OutFile zcp.exe
# then move zcp.exe to a directory on your PATH

Verify

zcp version
zcp --help

v0.0.20

Choose a tag to compare

@github-actions github-actions released this 30 Jun 16:32

zcp v0.0.20 Release Notes

Deleting a VM now releases its auto-assigned public IP

When a VM is created public, the CMP auto-assigns it a 1:1 public IP. Until now,
deleting the VM left that IP Allocated — orphaned and still billing — until someone
released it by hand. zcp instance delete now releases it as part of the delete, matching
the "Delete auto-assigned public IPs when deleting VM" option in the portal.

Highlights:

  • zcp instance delete releases the VM's auto-assigned public IP by default (sends
    delete_public_ip=true).
  • Manually-acquired and source-NAT IPs are untouched — they release only when their
    network/IP is removed.
  • Opt out with --delete-public-ip=false to keep the IP (e.g. when it's reused by NAT, a
    load balancer, or a shared network).

Added

zcp instance delete releases the auto-assigned public IP

zcp instance delete my-vm --yes                       # VM gone, its auto-assigned IP released
zcp instance delete my-vm --force --yes               # also expunge from the hypervisor immediately
zcp instance delete my-vm --delete-public-ip=false    # VM gone, keep the IP allocated

--delete-public-ip defaults to true. It only releases public IPs that the CMP assigned
when the VM was created — manually-acquired IPs and the network's source-NAT IP are never
touched by this flag (those release when you delete the network/IP). The interactive
confirmation prompt now says when the IP will be released:

WARNING: Delete "my-vm" is permanent and cannot be undone. Its auto-assigned public IP will also be released. [y/N]:

Changed

  • Behavior change: zcp instance delete used to leave the VM's auto-assigned public IP
    allocated (and billing) after the VM was deleted. It is now released by default. Add
    --delete-public-ip=false to keep the old behavior.

Upgrade notes

If you have scripts or automation that delete VMs and then reuse the freed public IP (for a
NAT rule, load balancer, or shared network), pass --delete-public-ip=false so the IP stays
allocated. Otherwise no changes are needed — the new default cleans up the leaked IPs you'd
previously have had to release manually with zcp ip release.


Installation

Linux / macOS / WSL (one-liner)

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Installs zcp to /usr/local/bin (you may be prompted for sudo). Set INSTALL_DIR to
choose another location, e.g. INSTALL_DIR="$HOME/.local/bin".

Windows (PowerShell)

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Installs zcp.exe to %LOCALAPPDATA%\Programs\zcp.

Manual download

Grab the binary for your platform from the
Releases page, make it executable, and put it on
your PATH.

OS Arch Asset
Linux x86_64 zcp-linux-amd64
Linux ARM64 zcp-linux-arm64
macOS Intel zcp-darwin-amd64
macOS Apple Silicon zcp-darwin-arm64
Windows x86_64 zcp-windows-amd64.exe
Windows ARM64 zcp-windows-arm64.exe
# Linux amd64 example
curl -Lo zcp https://github.com/zsoftly/zcp-cli/releases/latest/download/zcp-linux-amd64
chmod +x zcp
sudo mv zcp /usr/local/bin/zcp
# Windows amd64 example (PowerShell)
irm https://github.com/zsoftly/zcp-cli/releases/latest/download/zcp-windows-amd64.exe -OutFile zcp.exe
# then move zcp.exe to a directory on your PATH

Verify

zcp version
zcp --help

v0.0.19

Choose a tag to compare

@github-actions github-actions released this 21 Jun 14:46
1868417

zcp v0.0.19 Release Notes

Load balancers create cleanly, and instances are addressable by ID or name

This release fixes loadbalancer create (it never worked — the API requires an initial
rule), and makes every instance command accept the VM's ID, name, or slug, not just its
slug. It also paginates the instance list so large accounts see all of their VMs.

Highlights:

  • zcp loadbalancer create works — it now sends a required first rule
    (--public-port/--private-port/--algorithm) and can attach back-ends with --vm.
  • Refer to an instance by ID, name, or slug in every instance subcommand, with a clear
    error when a name is ambiguous.
  • instance list shows the ID column and returns all VMs (the list is now paginated).
  • reboot refuses a non-Running VM instead of silently no-op'ing.
  • Manage account access control — new zcp sub-user, zcp role, and zcp permission
    commands for creating sub-users, defining roles from permissions, and blocking/unblocking access.

Fixed

zcp loadbalancer create always failed

The request sent an empty rules array, which the API rejects — a load balancer must be created
with at least one rule. Create now builds that first rule from new flags:

zcp loadbalancer create --name my-lb --network <network-slug> --ip <ip-slug> \
  --billing-cycle hourly \
  --public-port 80 --private-port 8080 --algorithm roundrobin \
  --vm web-1 --vm web-2          # optional: attach back-ends

# add more rules later
zcp loadbalancer create-rule <lb-slug> --name api-rule \
  --public-port 8443 --private-port 443 --protocol tcp --algorithm leastconn

--public-port, --private-port, and --algorithm are required (the rule can't be formed
without them). --protocol defaults to tcp, --rule-name defaults to <lb-name>-rule, and
--sticky-method, --enable-tls, and --enable-proxy-protocol are optional.

instance list only returned the first page of VMs

The /virtual-machines endpoint is paginated, but the CLI fetched a single page — accounts with
more VMs than fit on one page silently lost the rest. The list now walks every page, which also
makes instance reference resolution (below) reliable.

Added

Address an instance by ID, name, or slug

Every instance subcommand — get, start, stop, reboot, reset, delete, logs, ssh,
tag-*, change-*, add-network, addons, purchase-addon — now accepts any unique reference
to the VM:

zcp instance reboot vm-1a2b3c        # by ID (vm_id)
zcp instance reboot my-web-server    # by name
zcp instance reboot my-web-server-1  # by slug

Match order is ID/vm_id, then name, then slug. If a name matches two VMs, the command lists the
matching IDs and asks you to pick one. Resolution checks your active region/project first and
falls back to an unscoped lookup when the reference isn't found there, so a globally-unique ID
or slug still works without --region.

ID column in instance output

zcp instance list and zcp instance get now show the instance ID (the value to copy for the
references above). -o json/-o yaml and --debug expand to the full set of columns.

Manage sub-users, roles, and permissions

Account access control is now scriptable. These are account-level commands — no --region/--project
needed.

# Permissions: the read-only catalog you build roles from
zcp permission list
zcp permission list --category "Virtual Machine"

# Roles: group permissions, then assign to sub-users
zcp role list
zcp role get service-administrator                 # shows its permissions + assigned users
zcp role create --name "VM Operator" \
  --permission virtual-machine-read --permission virtual-machine-manage
zcp role update vm-operator --permission virtual-machine-read --permission dns-read
zcp role delete vm-operator

# Sub-users: additional users under your account (addressable by id OR email)
zcp sub-user create --name "Jane Doe" --email jane@yourco.com \
  --password 'S3cret!pass' --role service-viewer --project default-9
zcp sub-user update jane@yourco.com --role service-administrator
zcp sub-user block jane@yourco.com                 # revoke access without deleting
zcp sub-user unblock jane@yourco.com
zcp sub-user delete jane@yourco.com

Notes: --permission on a role replaces the role's full set (it isn't additive), and role update
preserves any flags you don't pass. The predefined owner, service-administrator, and
service-viewer roles can't be edited or deleted. Sub-user --email must be a company address,
--password needs 8+ chars with mixed case, a number, and a symbol, and newly created sub-users start
blocked until you unblock them.

Changed

  • zcp instance reboot refuses a VM that isn't Running, e.g.
    instance "my-vm" is Stopped; it must be Running before it can be rebooted, instead of issuing a
    reboot the platform silently ignores.
  • zcp loadbalancer list and zcp instance list emit full objects for -o json/-o yaml
    rather than a flattened, all-string copy of the table — automation gets every field.
  • zcp auth validate honors ZCP_DEBUG like every other command.
  • Documentation URL is now https://docs.zcp.zsoftly.ca.

Upgrade notes

zcp loadbalancer create now requires --public-port, --private-port, and --algorithm.
Scripts that called create without them will need to add these flags (the command previously
failed at the API anyway). Everything else is additive — existing slug-based instance commands keep
working unchanged.

v0.0.18

Choose a tag to compare

@github-actions github-actions released this 19 Jun 06:45
432ff43

zcp v0.0.18 Release Notes

Region- and project-correctness: nothing runs unscoped

This release makes the CLI region- and project-aware everywhere, so it can no longer show
catalog entries that don't exist in your region or deploy a resource into the wrong zone. It also
fixes the SSH-key import flow and surfaces real API validation messages.

Highlights:

  • --region and --project are now mandatory for every region/project-scoped command, with
    per-profile defaults captured at zcp profile add (like aws configure).
  • Every list filters by region and project — no more cross-region clutter or un-deployable
    plans in the output.
  • zcp ssh-key import works (it required project + region all along).
  • 422 validation errors now show the field-level reason instead of a generic message.

Fixed

zcp ssh-key import always returned 500 … Attempt to read property "id" on null

The API derives the cloud provider from both project and region, and the CLI marked them
optional — so a call without them sent neither and the backend dereferenced a null. --project and
--region are now required (honoring ZCP_PROJECT/ZCP_REGION) and always sent. Verified
end-to-end: import → list → reference at VM create (the VM came back with the key attached) → delete.
--name is also validated client-side (≤ 20 chars) before the call.

API validation errors were swallowed

HTTP 422 responses returned only a generic Validation errors. This API puts the field-level
messages under data (not errors) and omits status, so they were dropped. They're now surfaced:

Error: ... API error 422: Validation errors — public_key: The public key has already been taken.
Error: ... API error 422: Validation errors — name: The name field must not be greater than 20 characters.

Catalog and resource listings returned every region's entries

zcp plan vm listed both YUL (ca*) and YOW (ci*) offerings; picking a wrong-region plan (an
Intel ci* plan in YUL) then failed to schedule ("no destination found") — the VM sat in
Starting, flipped to Error, and was cleaned up with no IP, which looked like a boot failure. The
CLI now sends filter[region]/filter[project] on every list, so you only ever see entries valid
for your region and project.

This does not fix the underlying CMP catalog, which still presents cross-region offerings as
selectable for a target region — that needs region-scoped offering filtering in the plan catalog.

Added

Region + project are required everywhere (with profile defaults)

Every region/project-scoped command now requires a region and a project. Satisfy them three ways:

# 1. Per-profile defaults (recommended) — captured at configure time, like `aws configure`
zcp profile add default        # prompts for token, default region, and default project

# 2. Environment variables
export ZCP_REGION=yow-1 ZCP_PROJECT=default-9

# 3. Per command
zcp instance list --region yow-1 --project default-9

Account-level commands have no region/project dimension and are exempt: dns, auth, profile,
region, project, cloud-provider, currency, billing-cycle, server, support, dashboard,
billing, product, store. (object-storage is scoped too; it uses the os-yul/os-yow
regions.)

Every list is now scoped to your region and project

Lists send filter[region]/filter[project] and return only what belongs to that region/project —
instances, networks, IPs, volumes, VPCs, Kubernetes clusters, load balancers, virtual routers,
autoscale groups, affinity groups, block-storage and VM snapshots, block-storage and VM backups,
object storage, and the full catalog (plan, template, iso, marketplace, storage-category).
For example, zcp plan vm --region yul-1 now lists only the ca* family YUL actually runs (the
Intel ci* plans appear only under yow-1).

zcp profile add captures a default region and project

Like aws configure, profile add now prompts for (and requires) a default region and a
default project, stored in the profile and used whenever --region/--project and
ZCP_REGION/ZCP_PROJECT are not set:

zcp profile add default \
  --bearer-token <token> --region yow-1 --project default-9

Changed

  • zcp instance create --ssh-key <name> now sends authMethod: "ssh-key" (and an empty
    password) alongside the key name, matching the Web UI's VM-create payload — previously the
    key name was sent without the auth-method flag, so SSH-key auth would not engage.
  • Docs/help clarified: SSH key names and the public-key material must be unique (re-importing
    the same key, even under a new name, is rejected); and a VPC alone cannot host a VM — create
    a network (tier) inside it (zcp network create --vpc …) and attach a VM to that tier
    (zcp instance add-network), since a bare VPC has no usable subnet.

Upgrade notes

This release requires a region and project for scoped commands. If you have scripts that ran
zcp instance list, zcp plan vm, etc. without one, either re-run zcp profile add to store
defaults, export ZCP_REGION/ZCP_PROJECT, or add --region/--project. Account-level commands
(listed above) are unaffected.

v0.0.17

Choose a tag to compare

@github-actions github-actions released this 18 Jun 04:00
12b32eb

zcp v0.0.17 Release Notes

Clearer errors, and no backend technology in output

This release is a CLI-usability and information-hygiene pass:

  • Actionable argument errors across every command that takes positional arguments
    (128 subcommands) — what's missing, the usage line, and the command's own examples.
  • Unknown subcommands now error (non-zero exit) instead of silently printing help.
  • Backend technology names are no longer exposed in any command output.
  • --cloud-provider is auto-detected and saved to your profile — you no longer pass it.

Added

object-storage object download

Objects can now be downloaded to a local file over the S3 protocol — previously the CLI
could upload, list, delete, and show metadata, but not fetch an object's contents.

zcp object-storage object download my-store my-bucket report.pdf            # → ./report.pdf
zcp object-storage object download my-store my-bucket images/logo.png --dest ./logo.png

--dest accepts a file path or a directory (writes the object's base name into it); it
defaults to the base name in the current directory.

Object versioning and raw bucket policies

  • zcp object-storage bucket versioning enable|suspend|status — S3 object versioning.
  • zcp object-storage bucket policy get|set|delete — read/set/remove a bucket's raw S3
    policy (set --file policy.json, or --file - for stdin) for fine-grained access.

Both verified live against Ceph RGW.

Tags, encryption, lifecycle, and a versioned-bucket fix

  • bucket tag / object tag — set/get/delete tags (--tag key=value).

  • bucket encryption status|enable|disable — default SSE-S3 encryption.

  • bucket lifecycle expire --days N [--prefix P] (plus get/delete) — auto-expire objects.

  • bucket empty and bucket delete --purge — remove all objects and versions. This
    fixes a real gap: a bucket that ever had versioning enabled couldn't be deleted, because
    its object versions/delete-markers blocked the REST delete.

  • bucket cors set --origin --method [--header --max-age] (plus get/delete) —
    cross-origin rules for browser apps.

RGW support for each was verified by probing the live endpoint with the S3 client before
building.

Versioning workflows, copy/move, stat, presigned upload, multipart cleanup

The object-storage S3 surface is now feature-complete (everything Ceph RGW supports
except object-lock, which needs a backend change at bucket creation). All of these
operations are CLI-only — they talk directly to the Ceph RADOS Gateway over the
S3 protocol and are not yet available via the ZCP REST API or the Web UI (only
instance and basic bucket CRUD are REST-backed and mirrored in the Web UI):

  • Versioning is usable: object versions, object download/delete --version-id,
    and object restore (undelete).
  • object copy / object move — server-side, no round-trip.
  • object stat (full S3 metadata via HEAD) and object put --metadata key=value.
  • object put-url — pre-signed upload URL (curl -T file "<url>").
  • bucket uploads list|abort — reclaim storage from failed large uploads.
  • bucket lifecycle expire --noncurrent-days / --abort-multipart-days — expire old
    versions and clean up stalled uploads.
  • policy/lifecycle/cors get now honor -o yaml.

Public buckets, shareable object URLs, and plan discovery

  • zcp object-storage bucket set-acl --acl public-read makes a bucket's objects
    anonymously downloadable (via an S3 bucket policy); --acl private reverts it.
  • zcp object-storage object url <slug> <bucket> <key> [--expires 24h] mints a
    pre-signed, time-limited link a client can use without credentials — even on a
    private bucket (max 7 days).
  • zcp plan object-storage lists the Object Storage plan slugs for create --plan.

These were verified end-to-end against live Ceph storage in YOW (create → bucket →
upload → make public → anonymous download → pre-signed URL → make private → delete).

Object-storage create is simpler

object-storage create --plan <slug> no longer needs --storage-category — the CLI
derives it from the plan (a mismatch previously failed with Invalid Storage Category).
Use an object-storage region (os-yul/os-yow) and a plan from zcp plan object-storage.

Changed

Helpful errors instead of accepts 1 arg(s), received 0

Before:

❯ zcp profile add
Error: accepts 1 arg(s), received 0

After:

❯ zcp profile add
Error: missing required argument: <name>

Usage:
  zcp profile add <name> [flags]

Examples:
  zcp profile add default
  zcp profile add prod --bearer-token <token>

Multi-argument commands name each missing placeholder:

❯ zcp acl create-rule
Error: missing required arguments: <vpc-slug>, <acl-name-or-id>

Usage:
  zcp acl create-rule <vpc-slug> <acl-name-or-id> [flags]

Examples:
  zcp acl create-rule my-vpc web-acl --number 1 --protocol tcp --start-port 80 --end-port 80 --cidr 0.0.0.0/0
  ...

Supplying too many arguments now reports too many arguments: expected N, got M with the
same usage/examples block.

This is purely a messaging change: which commands accept how many arguments is unchanged,
and all existing behavior on the success path is identical.

Unknown subcommands error instead of silently printing help

Running a command group with a bad subcommand used to print that group's help and exit 0,
hiding the typo and letting scripts treat a mistake as success. It now errors to stderr and
exits non-zero, with a message that adapts to the group. A group with one subcommand points
straight at it:

❯ zcp region lists
Error: unknown subcommand "lists" for "zcp region"

Run this instead:
  zcp region list    List available regions

Example:
  zcp region list

A group with several lists the valid subcommands and suggests the closest match:

❯ zcp profile shw
Error: unknown subcommand "shw" for "zcp profile"

Did you mean this?
	show

Available commands:
  add             Add or update a profile
  ...

Run 'zcp profile --help' for usage and examples.

Running a group with no arguments still prints help and exits 0.

--cloud-provider is auto-detected

Customers no longer pass a cloud provider. zcp auth validate (and zcp profile add)
detect the account's compute provider — the one whose catalog includes "Virtual
Machine" (nimbo in production) — and save it to the profile; every create command
reads it automatically. Object storage and DNS default to their own providers (ceph
and dns) automatically. The flag is hidden from help but remains available as an
override (along with ZCP_CLOUD_PROVIDER).

❯ zcp auth validate
Credentials are valid.
Cloud provider detected and saved to profile "default": nimbo

If it can't be determined, create commands say so with guidance instead of the old
terse "--cloud-provider is required".

All three provider values were confirmed against the live API: each region maps to
exactly one provider (yow-1/yul-1nimbo, defaultdns, os-yow/os-yulceph),
and real existing instances/volumes store cloud_provider: nimbo. As part of this,
zcp dns create now defaults --region to default (the DNS provider's only region)
so it is fully hands-off, and object-storage examples use object-storage regions
(os-yul/os-yow) — the previous yow-1/yul-1 DNS and object-storage examples were
wrong and would have failed.

Removed

Backend technology is no longer exposed in output

Display-only columns that revealed the underlying platform ("Cloud Stack", "Ceph", "Dns")
have been removed:

  • region list — dropped PROVIDER and COMING SOON
  • backup list, snapshot list, vm-backup list — dropped SERVICE
  • instance get — dropped the Service row
  • cloud-provider list — dropped DISPLAY NAME
  • dns list / dns show / dns create — dropped the DNS PROVIDER column/row, and hid the --dns-provider flag (which named the backend, e.g. powerdns)

These were informational only. Resource creation uses the provider/region slug, which is
retained, so no workflow changes. Billing/dashboard/project "SERVICE" columns are untouched —
they name billing categories (e.g. "Virtual Machine"), not backend technology.

Internal

  • New internal/commands/args.go provides drop-in exactArgs / minArgs / maxArgs /
    rangeArgs validators that replace cobra.ExactArgs / cobra.MaximumNArgs package-wide.
    Missing-argument names are derived from each command's Use line and examples from its
    Example field, so no per-command wiring is needed and new commands get the behavior for
    free by using the helpers.
  • EnforceSubcommandErrors (same file) walks the command tree once — called from root.go
    after all subcommands are registered — and installs the unknown-subcommand handler on every
    command group, so the behavior applies uniformly and to future groups automatically.

v0.0.16

Choose a tag to compare

@github-actions github-actions released this 17 Jun 16:30
9435308

zcp v0.0.16 Release Notes

VPC Subnets, Network ACLs, and Plan Discovery

This release makes the full three-tier VPC workflow possible from the CLI: create a VPC,
create subnets (tiers) inside it, and attach custom network ACLs — none of which worked in
v0.0.15. All fixes were confirmed against the live API (a three-tier VPC was built end-to-end
in the YUL region with per-tier ACLs as the release verification).


Added

VPC subnet (tier) creation — zcp network create --vpc

zcp network create --name web-tier --vpc my-vpc --acl web-acl \
  --gateway 10.30.1.1 --netmask 255.255.255.0 --billing-cycle hourly \
  --cloud-provider nimbo --region yul-1 --project default

The API requires type=Vpc with that exact casing — any other value passes validation but
silently creates a detached isolated network. The CLI now always sends the correct type and
rejects conflicting flags, so this trap is no longer reachable.

--acl attaches a custom ACL right after creation (the API has no attach-at-create
parameter); names are resolved to ACL IDs automatically.

zcp network get <slug>

Shows provider-side state that no command exposed before: CIDR, gateway, netmask, state,
VPC membership, and the attached ACL.

zcp plan network

Lists Network plans (pNet/iNet/l2Net) with slugs — the values for the new
--network-plan flag, which isolated/L2 network creation requires. The old --category
flow never worked against the live API (the categories endpoint returns an empty list);
--category is kept as an optional legacy flag.

zcp acl delete <vpc> <acl>

The platform now supports ACL list deletion; the CLI exposes it with name resolution and a
confirmation prompt.

ACL rule management — zcp acl rules / create-rule / update-rule / delete-rule

zcp acl create-rule my-vpc web-acl --number 1 --protocol tcp \
  --start-port 443 --end-port 443 --cidr 0.0.0.0/0 --action allow --traffic-type ingress

The rule endpoints live under .../network-acl-list/{id}/network-acl (singular). The order
of operations matters: create the ACL list first, then add rules one per request — an
embedded rules array on list creation is silently ignored. create-rule mirrors the live
validation (ports for tcp/udp, ICMP type/code for icmp) and resolves ACL names to IDs.
update-rule modifies a rule in place (rule ID preserved); --cidr accepts comma-separated
multi-CIDR lists on both create and update.

Fixed

  • acl replace / vpc acl-replace always failed with 403 — body field was aclSlug;
    the API requires acl_id (the ACL's ID). Both now send the right field, and --vpc
    resolves ACL names to IDs.
  • vpc get / vpc create showed blank CIDR/Status/Zone — now read from
    GET /vpcs/{slug} (CloudStack meta block) with a list fallback for older deployments.
  • All plan tables missing the SLUG columnvpc create --plan needs a slug, but
    plan router only showed UUIDs. Every plan table now includes SLUG.
  • Create-response decode crashis_default arrives as 0/1 on create but
    true/false on list; the decoder accepts both.
  • acl list blank columns — now shows ID / NAME / DESCRIPTION (matching the live API).
  • VPC network-address quirk — the API stores the address verbatim (10.30.0.1/16);
    the CLI warns when it isn't the canonical network base.

Known platform limitations (tracked in docs/roadmap.md)

  • 3 subnets per VPC (CloudStack vpc.max.networks) — the 4th create returns a generic 403.
  • VPC description is not persisted by the API.

v0.0.15

Choose a tag to compare

@github-actions github-actions released this 11 Jun 03:15
b250cc9

zcp v0.0.15 Release Notes

Network / VPC / VPN Bug Fixes

This release fixes a set of related bugs across the VPC, VPN, and network commands where the
CLI was sending or receiving incorrect JSON field names — causing commands to return blank
output, crash with a decode error, or silently ignore updates. All issues were confirmed against
the live API before release.


Fixed

VPC VPN gateway — fields always blank / crash on create

zcp vpc vpn-gateway create and list both returned empty rows.

Root cause: VPNGateway struct used wrong JSON tags (slug, publicIpAddress, vpcUuid,
vpcSlug, zoneName, status). The live API returns id, public_ip, vpc_id, vpc_name.
Additionally, the create endpoint returns {"data":null} instead of the created object.

Fix: Corrected all JSON tags; create now falls back to listing and returning the first
matching gateway when the API returns null data.

zcp vpc update — returns empty fields

PUT /vpcs/{slug} always responds with data:null. The command was unmarshaling null into
an empty struct and displaying blank values.

Fix: After any null PUT response, the command fetches the VPC via GET and returns the
real updated state.

zcp network update — crashes with JSON decode error

PUT /networks/{slug} returns data:[null] (an array with a null element). Attempting to
unmarshal that into a Network struct caused a hard error.

Fix: Same fallback-to-GET pattern; now handles both null and [null] responses without
crashing.

VPN customer gateway — all VPN config fields blank

zcp vpn customer-gateway create, update, and list all showed empty Gateway, IKE Policy,
CIDR, and other fields.

Root causes:

  • Three JSON tags were wrong: ipsec_preshared_keyipsecpsk, force_encapsulation
    forceencap, dead_peer_detectiondpd
  • SplitConnections was typed as bool but the API returns it as a string
  • Create API returns a metadata-only response (no VPN config); a subsequent GET is now made
    to the detail endpoint /vpn-customer-gateways/{slug}

Fix: All tags corrected; CustomerGatewayService.Get() added; create falls back gracefully
when CloudStack provisioning is still in progress.

VPN customer gateway — missing required API fields

The --ike-dh, --esp-dh, and --esp-pfs CLI flags were missing. The API rejects creates
without ike_dh and esp_pfs, so zcp vpn customer-gateway create could never succeed.

Fix: Three new flags added to both create and update.

VPN user list — Username column always blank

User.UserName had JSON tag userName; the API returns username.

Fix: Tag corrected; username now appears in zcp vpn user list.

zcp network update --description "" / zcp vpc update --description "" ignored

description in both network.UpdateRequest and vpc.UpdateRequest had omitempty.
Sending an empty string was silently dropped, making it impossible to clear a description.

Fix: omitempty removed from both structs.

zcp ip allocate — cannot assign to a project

No --project flag existed on zcp ip allocate, and the CreateRequest struct lacked the
project field entirely.

Fix: Field added to the struct; --project flag exposed on the command.

VPN customer gateway update — all fields blank + unusable without cloud context

zcp vpn customer-gateway update returned blank VPN config fields (gateway, IKE policy, CIDR,
etc.) because the PUT response is a metadata-only envelope — the VPN config fields are only
returned by the GET detail endpoint. Additionally, the update command was missing
--cloud-provider, --region, and --project flags, which the API requires on every PUT,
making the command return a server-side validation error in all cases.

Fix: Update() now always falls back to GET /vpn-customer-gateways/{slug} to retrieve
the full VPN configuration. The update command now accepts and validates --cloud-provider,
--region, and --project (resolving from env vars as with create).


New service methods (for Go library consumers)

Method Description
vpn.CustomerGatewayService.Get(ctx, slug) Fetch full VPN config for a single customer gateway
network.Service.Get(ctx, slug) Fetch a single network by slug

Installation

macOS / Linux / WSL:

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Windows (PowerShell):

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Verify:

zcp version
# zcp version v0.0.15

Full Changelog

v0.0.14...v0.0.15

v0.0.14

Choose a tag to compare

@github-actions github-actions released this 10 Jun 09:44

zcp v0.0.14 Release Notes

⚠ Breaking Changes — API package paths changed

This release moves all API service packages and the HTTP client out of internal/
into pkg/ so that external Go modules (such as the ZCP Terraform provider) can
import them directly.

CLI end users are not affected. The binary is identical to v0.0.12.

Migration guide for Go library consumers

Run a global find-and-replace in your project:

# macOS / BSD sed
find . -name '*.go' | xargs sed -i '' \
  's|github.com/zsoftly/zcp-cli/internal/httpclient|github.com/zsoftly/zcp-cli/pkg/httpclient|g'
find . -name '*.go' | xargs sed -i '' \
  's|github.com/zsoftly/zcp-cli/internal/api/|github.com/zsoftly/zcp-cli/pkg/api/|g'

# Linux sed
find . -name '*.go' | xargs sed -i \
  's|github.com/zsoftly/zcp-cli/internal/httpclient|github.com/zsoftly/zcp-cli/pkg/httpclient|g'
find . -name '*.go' | xargs sed -i \
  's|github.com/zsoftly/zcp-cli/internal/api/|github.com/zsoftly/zcp-cli/pkg/api/|g'

Then update your go.mod to reference v0.0.14:

go get github.com/zsoftly/zcp-cli@v0.0.14
go mod tidy

What moved

Old New
internal/httpclient pkg/httpclient
internal/api/acl pkg/api/acl
internal/api/affinitygroup pkg/api/affinitygroup
internal/api/apierrors pkg/api/apierrors
internal/api/autoscale pkg/api/autoscale
internal/api/backup pkg/api/backup
internal/api/billing pkg/api/billing
internal/api/billingcycle pkg/api/billingcycle
internal/api/cloudprovider pkg/api/cloudprovider
internal/api/currency pkg/api/currency
internal/api/dashboard pkg/api/dashboard
internal/api/dns pkg/api/dns
internal/api/egress pkg/api/egress
internal/api/firewall pkg/api/firewall
internal/api/instance pkg/api/instance
internal/api/ipaddress pkg/api/ipaddress
internal/api/iso pkg/api/iso
internal/api/kubernetes pkg/api/kubernetes
internal/api/loadbalancer pkg/api/loadbalancer
internal/api/marketplace pkg/api/marketplace
internal/api/monitoring pkg/api/monitoring
internal/api/network pkg/api/network
internal/api/objectstorage pkg/api/objectstorage
internal/api/plan pkg/api/plan
internal/api/portforward pkg/api/portforward
internal/api/product pkg/api/product
internal/api/project pkg/api/project
internal/api/region pkg/api/region
internal/api/response pkg/api/response
internal/api/server pkg/api/server
internal/api/snapshot pkg/api/snapshot
internal/api/sshkey pkg/api/sshkey
internal/api/storagecategory pkg/api/storagecategory
internal/api/store pkg/api/store
internal/api/support pkg/api/support
internal/api/template pkg/api/template
internal/api/userprofile pkg/api/userprofile
internal/api/virtualrouter pkg/api/virtualrouter
internal/api/vmbackup pkg/api/vmbackup
internal/api/vmsnapshot pkg/api/vmsnapshot
internal/api/volume pkg/api/volume
internal/api/vpc pkg/api/vpc
internal/api/vpn pkg/api/vpn

Packages that remain internal

These are CLI-only and are not part of the public API:

  • internal/commands
  • internal/config
  • internal/output
  • internal/version

Note on release tag format

Starting with this release, tags use the v prefix (v0.0.14) to align with
Go module conventions and the Terraform Registry. Previous tags (0.0.10.0.12)
are preserved but the old format will not be used going forward.


Installation

macOS / Linux / WSL:

curl -fsSL https://github.com/zsoftly/zcp-cli/releases/latest/download/install.sh | bash

Windows (PowerShell):

irm https://github.com/zsoftly/zcp-cli/releases/latest/download/install.ps1 | iex

Verify:

zcp version
# zcp version v0.0.14

Full Changelog

0.0.12...v0.0.14