# Servers (VMs) with `aral`

    aral server plans [--zone hel1]   # id, name, vcpu, ram_mb, disk_gb, price, where it fits now
    aral server regions               # slug (e.g. hel1), node_count
    aral server images                # os image slugs, e.g. ubuntu-24.04

`plans` with `--zone` prices the list for that zone and says whether each plan
fits there right now (`available` yes/no); without it, `available_zones` lists
the zones that can place it now. Table mode shows so'm; `-o json` keeps the raw
`price_monthly_cents` / `price_hourly_cents` (UZS tiyin, 100 tiyin = 1 so'm).
Every plan has a `trust_tier` (`platform` | `partner`); table mode marks partner
plans with `[partner hardware]` after the name.

## Custom sizes (pick vCPU / RAM / disk yourself)

    aral server pricing [--zone hel1]                         # unit prices + limits
    aral server quote --vcpu 4 --ram 8 --disk 160 --zone hel1 # exact price, fits?

Price per month = vcpu × unit + ram_gb × unit + disk_gb × unit (units from
`pricing`, billed hourly at ceil(monthly / 730)). `pricing --zone` clamps the
limits to what one node there holds now and adds `zone_max`. `quote` is the
authoritative number that will be charged: `-o json` gives
`{price_monthly_cents, price_hourly_cents, fits, errors[]}` — `errors` names
every violated bound (min/max, disk step, RAM per vCPU); `fits: false` with no
errors means the size is valid but no node in that zone can hold it now.

## Create

    aral server create --name my-vm \
      --plan-id <plan-uuid> --region hel1 --os-image ubuntu-24.04 \
      --ssh-key "ssh-ed25519 AAAA... me@host"

Or a custom size instead of a plan (exactly one of the two; all three numbers
together, RAM and disk in GB):

    aral server create --name my-vm --vcpu 4 --ram 8 --disk 160 \
      --region hel1 --os-image ubuntu-24.04 --ssh-key "ssh-ed25519 AAAA..."

A custom server shows a plan named "Custom 4 vCPU · 8 GB · 160 GB". A size no
node in the zone can hold is refused before anything is created, code
`zone_capacity` (params `{zone, vcpu, ram_gb, disk_gb}`) — pick a smaller size
or another zone; check first with `quote --zone`.

A `partner` plan runs on hardware owned and operated by an Aral Cloud partner,
who has physical access to it. Such a server has no public IPv4 (reach it with
`aral ssh`, the web console or tunnels) and its availability depends on the
partner. Creating one needs explicit consent: add `--accept-partner-hardware`.
Without it the API refuses with code `partner_consent_required` and nothing is
created. Only pass the flag when the user has agreed to partner hardware.

Returns `{id, status: "pending"}`. Poll `aral server get <id>` until
`status == "active"` (~2 min). `--ssh-key` is repeatable; at least one key is
required for access.

## Connect

    aral ssh <id>                       # opens the session (runs your ssh)
    aral ssh <id> --user root -- uptime # extra ssh args / a remote command after --
    aral server ssh <id>                # prints the ssh command instead

The login user is **ubuntu** on every image. `server get` fields that matter:
`node_ip` + `ssh_port` (the address that actually answers from the internet),
`ipv6` (direct, port 22), `ipv4` (node-internal — NOT reachable from outside),
`hardware_tier` (`platform` | `partner`) and `ssh_access` (`direct` | `proxy`).
First boot takes ~2.5 min before sshd answers; retry for up to 5.

`ssh_access: "proxy"` (every `partner` server) means there is no public SSH
port: SSH rides an authenticated tunnel through the platform. `aral ssh`
handles it; `server ssh` prints the equivalent, e.g.

    ssh -o 'ProxyCommand=aral ssh-proxy <id>' -o HostKeyAlias=aral-<id> ubuntu@aral-<id>

`-o json` adds `proxy_command` and `host_key_alias`. `aral ssh-proxy <id>`
pipes stdin/stdout to the server's sshd and is meant only as a ProxyCommand
(also usable in `~/.ssh/config`, scp, rsync -e, VS Code Remote). It uses the
saved login or `ARAL_TOKEN` (scope `servers:write`) — never put a token on that command line. Tunnel
refusals exit non-zero with the reason on stderr: 401 (log in again), 403
(not your server), 404 (no such server), 429 (too many tunnels, retry),
503 (server stopped or not reachable yet).

## Lifecycle

    aral server stop|start|reboot <id>
    aral server delete <id>

Delete first parks the server in `deleting` while a final disk copy is taken,
then `destroyed`. The copy is a custom image named "<name> (deleted copy)",
kept 7 days (billed at a reduced storage rate) — restore by creating a server
from it, or free it early by deleting the image.

## Resize

    aral server resize <id> --plan-id <plan-uuid>
    aral server resize <id> --vcpu 4 --ram 16 --disk 160

Same either-or as create. The disk can only grow. When the server's node has
no room for the new size the call is refused with code `node_capacity`.

## Errors for agents

With `-o json` a refused call prints `{"error", "code", "params"}` on stderr
and exits 1; branch on `code` (`zone_capacity`, `node_capacity`,
`token_spend_limit`, …), not on the sentence.

## Domains

    aral server domain list <id>      # HTTP(S)-only: the edge routes by hostname
Raw TCP/UDP never rides a domain — use port forwards / the node address.
