# Value formats

URL: https://scalws.com/docs/reference/value-formats/
Source: `docs/reference/value-formats.md` in the ScalWS repository

This page lists the exact forms ScalWS accepts for sizes, durations, CPU quotas, socket addresses, names, domains and paths in `scalws.yaml`, site files and on the command line.

Units are case-sensitive. Spaces before and after a size, a duration or a CPU quota are
ignored; names, domains and paths are taken as written.

## Sizes

Used by byte sizes such as
[`server.limits.max_request_body_bytes`](https://scalws.com/docs/reference/server-limits/#server.limits.max_request_body_bytes),
[`server.cache.memory`](https://scalws.com/docs/reference/server-cache/#server.cache.memory) and
[`tenants[].resources.memory`](/docs/reference/tenants/#tenants-.resources.memory), and by
`scalwsctl limit ram|io`.

A size is either a YAML integer (bytes) or a string of digits followed by an optional unit.
Spaces between the number and the unit are allowed, and a number without a unit is bytes.

| Unit | Bytes |
|---|---|
| none, `B` | 1 |
| `KiB` | 1024 |
| `MiB` | 1024² (1 048 576) |
| `GiB` | 1024³ |
| `TiB` | 1024⁴ |
| `KB`, `kB` | 1000 |
| `MB` | 1000² |
| `GB` | 1000³ |
| `TB` | 1000⁴ |

Binary and decimal units both mean what they say: `10MB` is 10 000 000 bytes, `10MiB` is
10 485 760 bytes.

| Accepted | Rejected |
|---|---|
| `1048576`, `"512"`, `16KiB`, `16 KiB`, `4GiB`, `10MB`, `2kB` | `1.5GiB` (no fractions), `-1` (no negative sizes), `GiB` (no number), `16kib`, `16K`, `16 kilobytes` (unknown unit), a value that overflows 64 bits |

ScalWS writes sizes back (for example `scalwsctl limit --persist`) in the largest binary
unit that divides them exactly, such as `2GiB` or `1536KiB`, else in bytes (`1000B`).

`scalwsctl limit io` takes a rate as a size followed by `/s`, for example `20MiB/s`.

The configuration schema used by ScalWS Console forms describes sizes with the units `B`,
`KB`, `MB`, `GB`, `KiB`, `MiB` and `GiB` only, so the console's forms may not accept
`TiB`, `TB` or `kB`; use a smaller unit there.

## Durations

Used by timeouts and intervals such as
[`server.limits.idle_timeout`](https://scalws.com/docs/reference/server-limits/#server.limits.idle_timeout),
[`server.acme.renew_before`](https://scalws.com/docs/reference/server-acme/#server.acme.renew_before) and
[`tenants[].applications[].cache.default_ttl`](/docs/reference/applications/#tenants-.applications-.cache.default_ttl).

A duration is a string: a whole number directly followed by one unit.

| Unit | Meaning |
|---|---|
| `ms` | milliseconds |
| `s` | seconds |
| `m` | minutes |
| `h` | hours |
| `d` | days |

| Accepted | Rejected |
|---|---|
| `500ms`, `10s`, `5m`, `1h`, `30d` | `10` (a bare number has no unit), `10 s` (space before the unit), `1h30m` (one unit only), `1.5s` (no fractions), `1w` (unknown unit), `10S` |

Some fields also limit the range, for example
[`server.ai.timeout`](https://scalws.com/docs/reference/server-ai/#server.ai.timeout) must be between `1s`
and `10m`.

### Durations in controller.yaml

`controller.yaml` uses plain integers instead:
[`instances.start_timeout`](https://scalws.com/docs/reference/controller/#instances.start_timeout),
`drain_timeout`, `kill_grace`, `restart_window` and the autoscaling windows and
[`autoscaling.cooldown`](https://scalws.com/docs/reference/controller/#autoscaling.cooldown) are whole
seconds (`drain_timeout: 30`);
[`autoscaling.lag_saturated`](https://scalws.com/docs/reference/controller/#autoscaling.lag_saturated) is whole
milliseconds (`lag_saturated: 5`).

## CPU quotas

Used by [`tenants[].resources.cpu`](/docs/reference/tenants/#tenants-.resources.cpu).

A CPU quota is a string: a whole number directly followed by `%`, in percent of one CPU.
`100%` is one full CPU, `400%` is four.

| Accepted | Rejected |
|---|---|
| `50%`, `200%`, `400%` | `4` (no `%`), `0%` (must be greater than 0%), `1.5%`, `200 %` |

`scalwsctl limit cpu` takes the same form; there the `%` may be left out (`50`).

### CPU lists in controller.yaml

[`instances.cpus`](https://scalws.com/docs/reference/controller/#instances.cpus) is a string of CPU numbers
and ranges separated by commas, for example `"0-5"`, `"0,2,4"` or `"0-3,6"`. A range
`a-b` needs `a <= b` and `b < 4096`.

## Socket addresses

Used by [`server.listeners[].address`](/docs/reference/server-listeners/#server.listeners-.address),
[`server.metrics.address`](https://scalws.com/docs/reference/server-metrics/#server.metrics.address) and
[`server.admin.remote.address`](https://scalws.com/docs/reference/server-admin/#server.admin.remote.address),
and by `scalwsctl --address`, `scalwsd --metrics-address` and `scalws-console --listen`.

A socket address is an IP address and a port, with no host names:

| Form | Example |
|---|---|
| IPv4 address and port | `127.0.0.1:8080`, `192.0.2.10:443`; `0.0.0.0:443` listens on every IPv4 address |
| IPv6 address in brackets and port | `[::]:443` listens on every IPv6 address |

Quote them in YAML (`address: "127.0.0.1:8080"`). Port `0` lets the operating system pick
a free port. Two listeners may not use the same address, and a listener may not use the
metrics address.

### FastCGI addresses

[`php.fastcgi`](https://scalws.com/docs/reference/runtime-php/#tenants-.applications-.runtime.php.fastcgi)
and `scalwsctl site add --php-fastcgi` take `unix:` followed by an absolute path
(`unix:/run/php/php8.3-fpm.sock`) or `tcp:host:port` with a port from 1 to 65535
(`tcp:127.0.0.1:9000`). `tcp:9000` is rejected.

### Upstream URLs

[`proxy.upstream`](https://scalws.com/docs/reference/runtime-proxy/#tenants-.applications-.runtime.proxy.upstream)
takes `http://host[:port]`, `https://host[:port]` or `unix:/absolute/path`. The host is a
name, an IPv4 address or an IPv6 address in brackets; the port defaults to 80 or 443 and
may not be 0. A path other than `/` and user information (`user@`) are rejected: requests
keep their original path.

### Networks

[`server.trusted_proxies`](https://scalws.com/docs/reference/server/#server.trusted_proxies) entries are an
IP address (`192.0.2.10`, one host) or a network in CIDR form (`192.0.2.0/24`). The prefix
is at most 32 for IPv4 and 128 for IPv6. Host names are rejected.

## Names

Tenant names ([`tenants[].name`](/docs/reference/tenants/#tenants-.name)), application
names ([`tenants[].applications[].name`](/docs/reference/applications/#tenants-.applications-.name))
and listener names ([`server.listeners[].name`](/docs/reference/server-listeners/#server.listeners-.name))
appear in metrics, logs and cgroup paths, so they are restricted:

* 1 to 63 characters;
* only `a`-`z`, `0`-`9`, `-` and `_`;
* the first character is a letter `a`-`z`.

| Accepted | Rejected |
|---|---|
| `shop`, `blog-example-com`, `api_v2` | `Shop` (upper case), `1password` (starts with a digit), `-shop`, `shop.example` (dot), a name of 64 characters |

Listener names must be unique, and application names must be unique within their tenant.

When a site file sets no `tenant` or `name`, ScalWS derives them from the domain: lower
case, every run of other characters becomes one `-`, leading and trailing `-` are dropped,
`s-` is put in front when the result does not start with a letter, and the result is cut to
59 characters. `shop.example.com` becomes `shop-example-com`, `1password.com` becomes
`s-1password-com`.

The instance id of `scalwsd --instance-id` is 1 to 32 characters of `a`-`z`, `0`-`9` and
`-`.

## Domains

Used by [`tenants[].applications[].domains`](/docs/reference/applications/#tenants-.applications-.domains),
[`server.listeners[].tls.certificates[].server_names`](/docs/reference/server-listeners/#server.listeners-.tls.certificates-.server_names),
and the [`domain`](https://scalws.com/docs/reference/site-file/#domain) and
[`aliases`](https://scalws.com/docs/reference/site-file/#aliases) of a site file.

A domain is normalized before it is checked and compared:

* letters are lower-cased (`Example.COM` is `example.com`);
* trailing dots are removed (`example.com.` is `example.com`).

The result must be at most 253 characters (without a leading `*.`) and consist of labels
separated by dots. Each label is 1 to 63 characters of `a`-`z`, `0`-`9` and `-`, and does
not start or end with `-`.

A leading `*.` makes a wildcard that covers exactly one label: `*.example.com` matches
`shop.example.com` but not `example.com` or `a.shop.example.com`. The part after `*.` must
have at least two labels.

| Accepted | Rejected |
|---|---|
| `example.com`, `Shop.Example.COM.`, `localhost`, `127.0.0.1`, `*.example.com`, `xn--bcher-kva.example` | `a..b` (empty label), `-a.example` (label starts with `-`), `a_b.example` (underscore), `foo.*.example` (`*` not first), `*.com` (wildcard over one label), `bücher.example` (use punycode), an empty string |

Internationalized domain names must be written in punycode (`xn--...`). A domain may be
served by only one application across the main file and all site files; the check reports
`domain "<name>" is already used by <tenant>/<app>`. A server name may be assigned to only
one certificate per listener.

## Paths

Fields that name a directory or a file to serve must be absolute paths:
[`tenants[].applications[].root`](/docs/reference/applications/#tenants-.applications-.root),
the `root` of the static and PHP runtimes, the Python runtime's
[`virtualenv`](https://scalws.com/docs/reference/runtime-python/#tenants-.applications-.runtime.python.virtualenv),
and PHP's [`fpm.binary`](https://scalws.com/docs/reference/runtime-php/#tenants-.applications-.runtime.php.fpm.binary).
A path starting with `/` is accepted on every platform, so a configuration can be checked
on another machine; the server checks that the path exists when it prepares the
configuration. Otherwise the check reports `<path> must be an absolute path`.

[`include`](https://scalws.com/docs/reference/top-level/#include) entries are relative to the directory of
the main configuration file. An entry may contain one `*`, and only in the file name
(`sites.d/*.yaml`); a `*` in a directory part and an entry that names a directory are
rejected.
