Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Built-in Functions

Relux provides built-in functions (BIFs) that are always available without imports. BIFs are divided into two categories based on their purity — whether they require a shell context to operate.

Purity

  • Pure BIFs do not interact with any shell. They can be called from pure functions, condition markers, overlay expressions, and regular shell blocks.
  • Impure BIFs require a shell context (they send input or match output). They can only be called inside shell blocks and regular (non-pure) functions.

“Pure” here means shell-independent, not side-effect-free — a pure BIF may still touch the outside world (which reads the filesystem, timestamp reads the system clock) and need not be deterministic (uuid, rand). What it may not do is require a shell. sleep, log, and annotate do require one and are impure, despite doing nothing shell-specific.

Shell-independent also does not mean infallible. A pure fn body may contain a pure match (<expr> = <pattern> / <expr> ? <pattern>), and a non-matching pure match is an assertion failure: a pure fn that runs one can fail the test through whichever runtime site called it (a let, an overlay value, or a shell-block call). The one exception is a marker condition, where a pure-eval failure makes the condition falsy rather than failing the test.

Pure BIFs

String

FunctionSignatureReturnsDescription
trimtrim(s)stringRemove leading and trailing whitespace from s.
upperupper(s)stringConvert s to uppercase.
lowerlower(s)stringConvert s to lowercase.
replacereplace(s, from, to)stringReplace all occurrences of from with to in s.
splitsplit(s, sep, index)stringSplit s by sep and return the part at index (0-based). Returns "" if the index is out of bounds. Errors if index is not a valid integer.
lenlen(s)stringReturn the byte length of s as a decimal string.
defaultdefault(a, b)stringReturn a if it is non-empty, otherwise return b.

Generators

FunctionSignatureReturnsDescription
uuiduuid()stringGenerate a random UUID v4 (e.g. "550e8400-e29b-41d4-a716-446655440000").
randrand(n)stringGenerate a random alphanumeric string of length n. Errors if n is not a valid integer.
randrand(n, mode)stringGenerate a random string of length n using the given charset mode. Modes: alpha, num, alphanum, hex, oct, bin. Errors if mode is unknown or n is not a valid integer.

System

FunctionSignatureReturnsDescription
available_portavailable_port()stringAllocate a free TCP port on 127.0.0.1 from the window between the OS privileged and ephemeral port intervals. Probed at allocation and never handed out twice while the allocating test is running; freed after the test’s cleanup completes.
whichwhich(name)stringSearch PATH for an executable named name. Returns the absolute path to the first match, or "" if not found. Checks that the file has an executable permission bit set. If name contains a path separator, checks that path directly instead of searching PATH.

A naive “bind port 0, read the number, close” probe returns a port from the OS ephemeral range — the same range the kernel uses for the source ports of outbound connections. Between picking the port and your service binding it, any client connection (a database bootstrap, a migration, a health check) can be assigned that number, and a closed client keeps the port in TIME_WAIT for 60 seconds. The result is a rare, unreproducible Address already in use at service start.

available_port therefore allocates strictly outside the ephemeral range. At first use relux reads the OS ephemeral interval (/proc/sys/net/ipv4/ip_local_port_range on Linux, net.inet.ip.portrange on macOS) and the privileged boundary, and allocates from the window between them, starting at a random offset. Every candidate is verified by binding it (ports in TIME_WAIT fail this probe and are skipped). A handed-out port is owned by the running test — across all its effects and functions — and is returned to the pool only after the test’s cleanup has completed, so within one relux run, no two concurrent tests can ever receive the same port.

The window can be narrowed with the manifest’s [available_ports] section (see the configuration chapter). On exhaustion of the window the call returns -1, like other BIF failures.

One residual caveat: this bookkeeping is per relux process, not a machine-wide reservation, so relux cannot stop an unrelated process on the machine — including another concurrent relux run invocation — from binding the chosen port before your service does. The allocation window makes that unlikely (the kernel never assigns those ports on its own), but it is not a guarantee.

Hashing

FunctionSignatureReturnsDescription
mnemonicmnemonic(s)stringDerive a stable, human-readable id from s, formatted adjective-noun-NNNN (e.g. "brave-otter-0042"). Deterministic across runs and across relux versions. About 2^29 distinct values – a readable label, not collision-proof and not for security.
sha1sha1(s)stringSHA-1 digest of s as 40-character lowercase hexadecimal.

Time

FunctionSignatureReturnsDescription
timestamptimestamp(fmt)stringCurrent UTC time formatted with a GNU date-style strftime string. Fractional seconds accept any width (%<N>f, %.<N>f); an unknown specifier is emitted verbatim.

timestamp always renders the current instant in UTC – there is no local-timezone mode. It deviates from chrono’s strftime in two ways: fractional-second specifiers accept any width, not just chrono’s fixed 3/6/9 (%1f..%9f, %.1f..%.9f, and widths above 9 right-pad with zeros), and an unknown specifier is emitted verbatim instead of being blanked out. Like uuid and rand, timestamp is non-deterministic across calls – it reads the system clock – but it is still a pure BIF because it never touches a shell.

timestamp("%Y-%m-%dT%H:%M:%SZ")  -> 2026-07-28T15:30:45Z
timestamp("%Y%m%d-%H%M%S")       -> 20260728-153045
timestamp("%s")                  -> 1753716645
timestamp("%H%M%S-%6f")          -> 153045-123456

Impure BIFs

Shell matching

FunctionSignatureReturnsDescription
match_promptmatch_prompt()stringMatch the shell prompt configured in Relux.toml. Advances the output cursor past the prompt.
match_okmatch_ok()stringMatch the shell prompt, send echo $?, match 0, and match the prompt again. Verifies the previous command exited with status 0.
match_not_okmatch_not_ok()stringMatch the shell prompt, verify the previous command exited with a non-zero status, and match the prompt again. The inverse of match_ok().
match_not_okmatch_not_ok(code)stringMatch the shell prompt, verify the previous command exited with a specific non-zero status code, and match the prompt again. Like match_exit_code(code) but also asserts the code is non-zero.
match_exit_codematch_exit_code(code)stringSend echo $?, match code, and match the prompt. Verifies the previous command exited with the given status. code is passed as a bare literal (e.g. match_exit_code(1)).

Control characters

FunctionSignatureReturnsDescription
ctrl_cctrl_c()""Send ETX (0x03) — interrupt the current process.
ctrl_dctrl_d()""Send EOT (0x04) — signal end of input.
ctrl_zctrl_z()""Send SUB (0x1A) — suspend the current process.
ctrl_lctrl_l()""Send FF (0x0C) — clear the terminal screen.
ctrl_backslashctrl_backslash()""Send FS (0x1C) — send SIGQUIT to the current process.

Timing

FunctionSignatureReturnsDescription
sleepsleep(duration)""Pause execution for duration. Accepts humantime format: 500ms, 2s, 1m30s, etc. Errors if the duration is invalid.

Logging

FunctionSignatureReturnsDescription
loglog(message)stringEmit message to the event log and HTML report. Returns message.
annotateannotate(text)stringEmit text as a progress annotation. Renders inline on the live progress line (between the surrounding fn-call ( and )) and is recorded as an event in the structured log. Returns text.