Builtin index
Every Mix builtin grouped by category, generated from the binary's own mix builtins --json. See the linked topical page for prose and examples; mix what NAME prints a one-line description of any single builtin (or keyword), and mix help prints a compact names-only summary.
For incremental HTTP responses, see http_stream:
a scoped line callback with early stop, independent idle and whole-call
timeouts, and partial-byte diagnostics. The same idle_timeout option is
available on http_get, http_post and http_request.
Some builtins are feature-gated on the mixos-lib-mix crate (json, regex, toml, yaml, datetime, url, crypto, http, sqlite, archive, dkim, markdown, datastar, xml, mime) so embedders can pull only what they need — the mix binary turns them all on.
Machine-readable discovery (metadata_schema 1)
From 0.97.0, required_capabilities lists additional unconditional capability
classes beyond capability. File transfers use this to require filesystem
access even without an options map. Evaluator policy checks and mix lint
enforce the combined set; conditional_capabilities remains option-dependent.
Argument entries also carry hint (instructional text or null), including
encode-first guidance for file payloads; this is additive within metadata_schema 1.
Since 0.29.0 every builtin carries a structured contract — per-argument names/kinds/optionality, derived min/max arity, a return shape, effect flags, an operational-failure mode, and any conditional capabilities. The human signature shown by mix builtins <name> is derived from the same contract, so it can never drift from the machine metadata.
mix builtins --json— the full table as a JSON array; each entry carriesmetadata_schema(1),kind(builtinorstatementfor print/eprint),name,category,capability(kebab class, orstatement),conditional_capabilities([{option, capability}]),description,signature,arity({min, max, exact}—max: null= variadic;exactis normallynull, but when a builtin's accepted argument counts are NOT a contiguous range it lists them exhaustively, e.g.randomhasexact: [0, 2]— an arity checker must useexactwhen present),args([{name, required, variadic, kind}]),returns,effects, andoperational_failure.mix builtins --data— the same logical array as strict-data Mix source (parse withdata_parse/load_data);nullbecomesnil.- Value shapes (
kind/returns) are{type: "any"|"nil"|"bool"|"number"|"string"|"bytes"|"buffer"|"function"}scalars,{type: "list", items: shape},{type: "map", shape: name-or-null, fields: [...]}, or{any_of: [shapes...]}unions. effectsflags:must_use(discarding the result hides a failure —run_rc,ssh_run,http_*, ...),blocking(unbounded/external wait: process, network, stdin, sleep),shell(backed by/bin/sh),mutates_args(push,buffer_push, ...),terminates(exit,panic).operational_failure: how I/O-level failure is reported —not-applicable(pure),raises(catchable error),returns-result(encoded in the returned value, e.g.ok:false/status:0/ exit code),terminates. Argument-validation errors always raise regardless.- Arity in the contract is the documented surface; a few older builtins tolerate surplus arguments at runtime.
mix lintchecks calls against the contract, which is deliberately stricter. - Argument type gate and nil (0.103.x): the evaluator checks argument types against the contract for the side-effect classes (fs/process/network/bus) in every mode, and for pure builtins under strict mode. A
nilVALUE is checked like any other value — there is no blanket nil exemption, sowrite_file(nil, "x")andmkdir(nil)raise TYPE_MISMATCH instead of coercing nil into a literal"nil"path. An optional argument that intentionally treats an explicitnilas "omitted/default" declares it in the contract asany_of(…, nil)(e.g.run_argv/exists/write_atomic/stat/walkopts,hash_filealgo,bytes_to_stringopts,normalizeform,sliceend); that is the ONLY way a nil reaches the builtin.anyargs accept nil because they accept everything. A nil in a plainstring/number/mapslot raises in the same modes as any other wrong type.
The tables below are generated:
docs/_build/gen-builtins-md.mixwrites one section per category, one row per entry ofmix builtins --json, in the binary's order.cargo test -p system --test man_pagesfails when the committed tables differ from the binary it built, so after adding or changing a builtin runmix docs/_build/gen-builtins-md.mix --mix target/debug/mix. Rows of kindstatement(eprint, and the Bus formssend,emit,on,reply, ...) are keyword forms listed beside the builtins; see bus.
string — see strings
length Length of a string (codepoints), list/map (elements), or bytes/buffer (bytes, v0.64.0)
len Alias for length()
upper Convert string to uppercase
lower Convert string to lowercase
left Return leftmost N characters
right Return rightmost N characters
substr Extract substring by codepoint position and length (splits emoji/combining; see grapheme_substr)
pos Find first position of needle in haystack (1-based, 0=not found — so if pos(..) reads correctly, since 0 is falsy). 0-based twin: index_of(), which takes its args the other way round and is NOT safe in a condition
lastpos Find last position of needle in haystack (1-based, 0=not found — safe in a condition, 0 is falsy). 0-based twin: last_index_of(), which takes its args the other way round
strip Remove leading/trailing whitespace, or codepoints in charset: strip(s[, charset]) (0.63.0 — the 2nd arg was silently IGNORED before)
trim Alias for strip(): trim(s[, charset]) — charset is a SET of codepoints to strip from both ends (0.63.0; was silently ignored). One-sided: ltrim/rtrim
replace Replace all occurrences of old with new in string
replace_must replace() that REFUSES a no-op: raises NEEDLE_ABSENT when old does not occur, and NEEDLE_COUNT when {count: n} is given and the occurrence count differs. Optional {path} names the file in the message. The form to use in a read_file -> edit -> write_file chain (0.90.0)
split Split string into list by delimiter (default: space)
join Join list into string with delimiter (default: space)
starts_with Test if string starts with prefix
ends_with Test if string ends with suffix
contains Test if string/list contains a value, or a bytes/buffer contains a byte needle (needle: bytes/buffer/string/byte number, non-empty; v0.70.0) — the correct yes/no test, and what to use instead of a bare index_of() in a condition
repeat Repeat string N times
lpad Left-pad string to width (codepoint count; see lpad_w for display cells). Optional 3rd arg is the fill character, default space: lpad(s, 12, "0") (v0.54.0)
rpad Right-pad string to width (codepoint count; see rpad_w for display cells). Optional 3rd arg is the fill character, default space (v0.54.0)
lpad_w Left-pad to width in terminal display CELLS (UAX #11; CJK/emoji=2) — aligns wide-char columns. Optional 3rd arg is the fill character (must be 1 cell wide), default space (v0.54.0)
rpad_w Right-pad to width in terminal display CELLS (UAX #11; CJK/emoji=2) — aligns wide-char columns. Optional 3rd arg is the fill character (must be 1 cell wide), default space (v0.54.0)
reverse Reverse a string (by codepoint; splits emoji — see grapheme_reverse), list, or bytes/buffer (byte-wise → a new value-semantic bytes, v0.70.0)
words Count whitespace-delimited words in string
word Extract Nth word from string (1-based)
ord Unicode codepoint of the FIRST character: ord("A") -> 65, ord("é") -> 233. Empty string raises. Inverse: chr()
chr The 1-character string for a Unicode codepoint: chr(65) -> "A", chr(10084) -> "❤". Surrogates (D800-DFFF) and >0x10FFFF raise, same rule as \u{...}. Inverse: ord()
normalize Unicode normalisation (UAX #15): normalize(s[, form]) with form "NFC" (default), "NFD", "NFKC" or "NFKD" (case-insensitive; nil selects NFC; anything else raises VALUE_ERROR). Makes canonically-equivalent text compare equal: a decomposed e + combining acute (macOS filenames, NFD) equals the precomposed é after normalize(); NFKC also folds compatibility forms (fullwidth letters, ligatures: normalize("fi", "NFKC") -> "fi"). Emoji and ZWJ sequences pass through unchanged (v0.92.0)
mix_tokens How Mix itself tokenises Mix source, for tools that read .mix files: mix_tokens(source[, {comments: bool}]) -> [{kind, text, line, col}] from the real lexer, in source order; nothing is run. kind: number, string (a quoted literal), word (a bare word), interp_string, heredoc, variable, command_sub, keyword, operator, punct, newline, and comment (only with comments:true; # and -- comments). text is the exact source span (a heredoc includes its body and terminator); line and col are 1-based, col in characters. Newlines inside (), [] and {} are not tokens. Source that does not lex raises MIX_SOURCE_LEX with details {line, col} (v0.117.0)
before Text before the FIRST delim: before(s, delim) -> string | nil (nil when delim absent; "" is a real result — delim at the start). Empty delim raises
after Text after the FIRST delim: after(s, delim) -> string | nil (nil when delim absent; "" when delim at the end). Empty delim raises. Want a default? after($s, "=") or ""
before_last Text before the LAST delim -> string | nil (nil when absent). Empty delim raises
after_last Text after the LAST delim -> string | nil (nil when absent) — basename/extension: after_last(path, "/"), after_last(name, "."). Empty delim raises
split_once Split at the FIRST delim: split_once(s, delim) -> [head, tail] | nil (nil when absent — never a 1-element list). Empty delim raises
rsplit_once Split at the LAST delim -> [head, tail] | nil (nil when absent). Empty delim raises
between Text after the first a and before the NEXT b: between(s, a, b) -> string | nil (nil if either marker absent, in that order). Empty a or b raises
strip_prefix s without a leading p, else s UNCHANGED (never nil — "nothing to strip" is an answer). Empty p -> unchanged. Kills the starts_with+substr idiom
strip_suffix s without a trailing x, else s UNCHANGED (never nil). Empty x -> unchanged
replace_first Replace the FIRST occurrence of old; old absent -> s unchanged. Empty old mirrors replace(): inserts new at the start (replace_first("ab", "", "X") is "Xab")
count_of Non-overlapping occurrences of needle in s; 0 for an empty needle. (The HOF count(list, pred) is a different builtin)
ltrim Strip leading whitespace, or leading codepoints in charset: ltrim(s[, charset]) — PHP-style charset is a SET of codepoints, not a prefix string
rtrim Strip trailing whitespace, or trailing codepoints in charset: rtrim(s[, charset])
lines Split into lines: \n-separated, ONE trailing \r stripped per line (CRLF and LF both work; a lone \r is not a terminator), exactly one trailing empty element dropped (the final newline). lines("") -> []; "a\n\n" -> ["a", ""]. Native since 0.63.0 (was a prelude fn that kept \r and the trailing "")
fields awk-style fields: split on whitespace RUNS, no empties; fields("") -> []. 0-based access fields(s)[2]; the 1-based single-field form is word(s, n)
chars Codepoints as 1-char strings (grapheme_* builtins exist for clusters). Native since 0.63.0 (was a prelude fn)
last_index_of 0-based codepoint index of the LAST occurrence in a string, or last index of a value in a list; -1 if absent. The 0-based twin of lastpos() (args reversed). List search compares with == — SCALAR elements only, exactly like index_of (a map/list element never matches; deep_eq is the structural comparison). ⚠ Like index_of, NEVER bare in a condition: -1 is truthy, 0 is falsy
template Substitute single-brace {key} placeholders in a string from a map
word_wrap Wrap text to a column width (codepoint budget; see word_wrap_w for display cells)
word_wrap_w Wrap text to a column width in terminal display CELLS (UAX #11; CJK/emoji=2)
markdown_escape Escape markdown metacharacters in a string
markdown Render CommonMark + GFM markdown (tables, strikethrough, task lists, footnotes) to HTML; raw HTML is escaped and unsafe URL schemes neutralised (requires markdown feature)
html_escape Escape & < > " ' for HTML element text + quoted attribute values (not JS/CSS/URL/srcdoc contexts)
sanitize Make untrusted bytes safe for one-line diagnostics: collapse line breaks (incl. U+2028/9) to spaces, replace C0/C1 controls and Trojan-Source bidi/zero-width chars with '?'
re_match Subject-first regex test: re_match(s, pattern) -> bool, true if pattern matches anywhere in s
re_find All matches as {match, start, end[, groups]} maps with CODEPOINT offsets (compose with substr/slice/index_of; the deleted legacy regex_find answered in UTF-8 BYTE offsets). [] when none
re_replace Replace ALL matches: re_replace(s, pattern, replacement) — subject FIRST; $1/${name} backrefs in replacement
re_replace_must re_replace() that REFUSES a no-op: raises NEEDLE_ABSENT when the pattern does not match, and NEEDLE_COUNT when {count: n} is given and the match count differs. Optional {path} names the file in the message (0.90.0)
re_split Split s on each match of pattern (subject first)
grep_lines Lines of text matching pattern (subject first; regex when enabled, else substring) — the line filter (its pattern-first predecessor grep() was deleted in release B)
csv_parse Parse CSV string into a list of header-keyed row maps. delim is exactly ONE ASCII byte (default ","; empty/multi-byte/non-string/nil and NUL/CR/LF/quote raise TYPE_MISMATCH — never a silent default or truncation)
ini_parse Parse INI string into nested map of sections
xml_parse Parse a strict-XML string (or bytes, e.g. an HTTP body) into a Value tree (requires xml feature). Default simple mode is the SOAP/RSS consumer shape: {RootName: …} with namespace prefixes stripped, attributes as @name keys, repeated sibling elements collapsed to a list, a leaf element's text as its value, mixed text under #text, xmlns declarations dropped. Pass {mode:"tree"} for full fidelity: nodes are {name, attrs, children} with prefixes + xmlns preserved and text children as plain strings. Strict XML only — real-world HTML is tag soup and will NOT parse.
url_parse Parse URL into {scheme, host, port, path, query, fragment}
url_decode Percent-decode a URL/form-encoded string ('+' → space)
url_encode Percent-encode a string for use in a URL/form body
rfc2047_decode Decode RFC 2047 encoded-words in a mail header value (=?charset?B/Q?data?=) to a plain string. Honours the charset token (utf-8/us-ascii/iso-8859-1; anything else falls back to UTF-8-lossy), joins adjacent encoded-words at the BYTE level per §6.2 so a character split across two words survives, and passes malformed words through literally rather than losing the header. Accepts string/bytes/buffer (v0.67.0)
rfc2047_encode Encode a header value as RFC 2047 encoded-words in UTF-8, or return it UNCHANGED when it is already plain ASCII with no "=?" (§5: encode only when needed). Optional {encoding: "B"|"Q"}, default B. Words are kept within the §2 75-character limit and split on CHARACTER boundaries so each stays independently decodable (v0.67.0)
parse_query Parse a k=v&k2=v2 query/form string into a map (url-decoded, last-wins)
parse_form Parse an x-www-form-urlencoded body into a map (alias of parse_query)
byte_length Length of a string in raw UTF-8 bytes (the pre-0.8.0 length() value for strings)
byte_pos Byte offset of needle in haystack (1-based, 0=not found) — byte twin of pos(), 0-based twin byte_index_of(). Safe in a condition (0 is falsy)
byte_lastpos Last byte offset of needle in haystack (1-based, 0=not found) — byte twin of lastpos()
byte_index_of Byte offset of needle in string (0-based, -1=not found) — byte twin of string index_of(), 1-based twin byte_pos(). ⚠ NEVER use bare in a condition: -1 is truthy, 0 is falsy (MIX-W2305)
grapheme_count Count grapheme clusters (user-perceived chars: emoji/flags/combining count as 1)
grapheme_substr Substring by grapheme cluster position and length (won't split emoji/combining marks)
grapheme_reverse Reverse a string by grapheme cluster (emoji/combining-safe, unlike reverse())
display_width Terminal display width in cells (UAX #11; CJK/emoji=2, combining=0, East-Asian-ambiguous=1)
type — see numbers
deep_eq Structural equality for any two values: maps compare by key set + deep_eq values (insertion order IGNORED), lists elementwise in order, scalars as ==. The answer == cannot give for maps/lists — since v0.68.0 ==/!= RAISES when both sides are a map or list, and names this builtin. Caveats, inherited from ==: a FUNCTION value is never equal (even to itself — a callback-bearing map is not deep_eq its own copy) and Buffer-vs-Bytes is false (freeze() first). Raises past 512 nesting levels
type Return type name: string, number, bool, list, map, nil
to_number Convert value to number (nil if not numeric)
to_string Convert value to its string representation
is_number Test if value is numeric or a numeric string
is_empty Test if string/list/map is empty, or value is nil ("0" is NOT empty)
has_builtin Does THIS mix have the named builtin? has_builtin(name) -> bool. mix builtins NAME exits 0 for any name and cannot answer this; use for feature gates and compat shims: if not has_builtin("ws_connect") then die "needs mix >= 0.74" end. A feature-gated name absent from this build still reads true (the binary knows it, and calling raises 'requires the X feature') (v0.78.0)
mix_version The language runtime version as structured data: {major, minor, patch, string} — so a version gate never parses --version text (v0.78.0)
math — see math
round Round to nearest integer, half away from zero; round(x, n) to n decimal places (n<0 rounds to tens/hundreds) (v0.19.0) floor Round down toward -inf; floor(x, n) to n decimal places (v0.19.0) ceil Round up toward +inf; ceil(x, n) to n decimal places (v0.19.0) trunc Truncate toward zero (drop the fraction); trunc(x, n) to n decimal places (v0.19.0) abs Absolute value (v0.19.0) sign Sign of x: -1, 0, or 1 (±0→0, NaN→NaN) (v0.19.0) band Bitwise AND of two integers. Both arguments must be exact integers within ±2^53 (the range f64 represents without loss); a fraction, an infinity, a NaN or an out-of-range magnitude raises rather than silently truncating. Chiefly for permission bits: band(stat(p)["perm"], 0o111) != 0 asks whether any execute bit is set (v0.46.0) bor Bitwise OR of two integers; same exact-integer domain as band (v0.46.0) bxor Bitwise exclusive-OR of two integers; same exact-integer domain as band (v0.46.0) bnot Bitwise NOT (one's complement) over 64-bit two's complement: bnot(0) is -1. Same exact-integer domain as band (v0.46.0) bshl Shift left by n bits (n in 0..63). Raises rather than wrapping if the result leaves the exact-integer range (v0.46.0) bshr Arithmetic shift right by n bits (n in 0..63); the sign bit is replicated, so bshr(-8, 1) is -4 (v0.46.0) sqrt Square root (negative→NaN) (v0.19.0) cbrt Cube root (defined for negatives) (v0.19.0) pow Raise to a power: pow(base, exp) = base^exp (v0.19.0) exp e raised to the x (v0.19.0) ln Natural logarithm, base e (ln(0)→-inf, ln(neg)→NaN) (v0.19.0) log10 Base-10 logarithm (v0.19.0) log2 Base-2 logarithm (v0.19.0) log Logarithm in an arbitrary base: log(x, base) (v0.19.0) min Smallest of the number arguments, or of a single list argument: min(a, b, …) | min(list). Lexicographic when all args are strings; NaN-skipping (v0.19.0) max Largest of the number arguments, or of a single list argument: max(a, b, …) | max(list). Lexicographic when all args are strings; NaN-skipping (v0.19.0) clamp Constrain a number to a range: clamp(x, lo, hi) (errors if lo > hi) (v0.19.0) hypot Euclidean distance sqrt(x²+y²) computed without intermediate overflow: hypot(x, y) (v0.19.0) sin Sine of x in radians (v0.19.0) cos Cosine of x in radians (v0.19.0) tan Tangent of x in radians (v0.19.0) asin Arcsine in radians (domain [-1, 1], else NaN) (v0.19.0) acos Arccosine in radians (domain [-1, 1], else NaN) (v0.19.0) atan Arctangent in radians (v0.19.0) atan2 Angle in radians of the point (x, y): atan2(y, x) (v0.19.0) pi The constant π (v0.19.0) e Euler's number e (v0.19.0) random random() -> float in [0,1); random(min, max) -> integer in [min, max] inclusive (v0.23.0)
list — see collections
push Append value to end of list (mutates) pop Remove and return last element of list (mutates) shift Remove and return first element of list (mutates) sort Return sorted copy of list (all-number lists sort numerically; else lexicographic) index_of Find 0-based index of value in a list, of a needle substring in a string (codepoint-based), or of a byte needle in a bytes/buffer (byte offset; needle: bytes/buffer/string/byte number, non-empty; v0.70.0); -1 if absent. 1-based twin: pos() (args reversed). ⚠ NEVER use bare in a condition — -1 (absent) is TRUTHY and 0 (first position) is FALSY, so both answers invert; use contains() for yes/no or compare >= 0 (MIX-W2305) unique Return list with duplicates removed range Generate list of numbers from start to end with optional step. Bounds/step must be whole numbers within i64 — fractional or oversized values raise VALUE_OUT_OF_RANGE instead of silently saturating (strict since v0.59.0) flat Flatten nested lists into a single list concat Concatenate 2+ lists into one new list (one level; each arg must be a list) slice Sub-sequence [start, end): negative indices and out-of-range clamp, a reversed range is empty (v0.2.0). Slices a list (elements), string (codepoints), or bytes/buffer (bytes → a new value-semantic bytes, v0.64.0) take First N items of a list (negative N = last N) (v0.2.0); also string (codepoints) and bytes/buffer (bytes → a new value-semantic bytes, v0.70.0) drop Skip first N items of a list (negative N = drop last N) (v0.2.0); also string (codepoints) and bytes/buffer (bytes → a new value-semantic bytes, v0.70.0) zip Pair two lists element-wise into [a, b] tuples (v0.2.0)
map — see collections
keys Return list of map keys values Return list of map values has_key Test if map contains a key merge Merge two maps (second wins on conflicts) delete Return map with key removed
buffer — see buffer
buffer Create a reference-semantic MUTABLE byte buffer (the escape hatch from value semantics for large binary/audio/video). buffer() or buffer(nil) empty; buffer(n) n zero bytes; buffer(string) UTF-8; buffer(bytes|buffer) independent copy; buffer([items]) flat splice of int 0-255 / string / bytes / buffer. Append with buffer_push (O(1) amortized, aliases share); freeze() to a value-semantic bytes (v0.26.0) buffer_push Append bytes to a buffer IN PLACE (reference-semantic: every alias sees the growth). Each item is an int 0-255, string (UTF-8), bytes, or buffer. Self-append-safe (v0.26.0) buffer_get Byte at 0-based index i as a number 0-255, or nil if out of range (v0.26.0) buffer_set Write byte (0-255) at 0-based index i, in place; errors if i is out of range — grow with buffer_push first (v0.26.0) freeze Snapshot a buffer to a value-semantic bytes (a copy of the current content) — the bridge into write_file/hash/base64/http (v0.26.0)
io — see io
read_file Read entire file contents as string
fs_watch Register Linux inotify watch; returns evaluator-owned opaque handle. Delivers fs.changed via $event.args; bounded, overflow requires rescan; no polling fallback
fs_unwatch Cancel watch and pending changes; wake fs_wait with FS_WATCH_CANCELLED
fs_wait Suspend until a filesystem batch; cancellation-safe; refused in serve and expression modes
dir_open Open a retained native directory root; evaluator-owned numeric handle. Refuses symlink components. Linux openat2 required; no fallback (v0.108.0)
dir_rename Move a regular file or directory between relative paths beneath a retained root, atomically refusing overwrite. Landlock ABI 2 and erratum 3 required. Refuses symlink sources and escaping parents. Root pathname checked before operation; retained inode remains the containment boundary during concurrent root moves (v0.108.0)
dir_close Close an evaluator-owned retained directory root; invalid or closed handles raise (v0.108.0)
read_file_bytes Read file contents as raw bytes. Optional 2nd arg caps the read: read_file_bytes(path, 8192) reads at most 8192 bytes (header-sniffing without slurping a huge file) (v0.3.1; cap v0.17.1)
read_lines Read file as a list of lines (trailing newline stripped, empty last line dropped) (v0.2.3)
load_data Read + parse a strict-data .mix file (bare-key k: v, the zones.mix/conf.mix form) into a Value — the non-executing twin of source/include, for substrate-internal data that must NOT run as code (v0.9.0)
write_file Write a string, bytes or buffer to file (creates/overwrites); bytes/buffer are written verbatim, a string as UTF-8. Nothing else is coerced — nil/map/list/function/number/bool raise TYPE_MISMATCH before the file is opened (encode with json_encode/data_encode/to_string first)
write_new Atomically create a new file with mode. write_new(path, content, 0o600) — mode as a value (octal literal) or octal string "0600"; fails if path exists; mode applied at creation (no umask race). content is a string, bytes or buffer (nothing else is coerced — wrong types raise TYPE_MISMATCH before the O_EXCL open)
write_atomic Replace or create a file so every reader and every crash sees the old complete file or the new complete file, never a partial one: write_atomic(path, data[, {durability, mode, max_bytes}]). data is a string, bytes or buffer (nothing else is coerced). A temp is claimed O_EXCL beside the target (same filesystem), written, optionally synced, and renamed over the target; any failure before the rename removes the temp and leaves the target untouched. durability "none" (default: atomic, NOT durable across power loss) | "file" (fsync the file before the rename) | "full" (also fsync the directory after it). An existing target keeps its mode and owner (raises rather than silently change the owner); a new file gets 0o666 & ~umask; mode sets it exactly. A symlink path replaces the file it names and keeps the link. max_bytes raises WRITE_TOO_LARGE before touching disk. A killed process can leave a hidden .NAME.mixtmp-* temp beside the target, never a partial target
append_file Append a string, bytes or buffer to file (created if missing); bytes/buffer append verbatim, a string as UTF-8. Nothing else is coerced — nil/map/list/function/number/bool raise TYPE_MISMATCH before the file is opened
exists Test if path exists. FOLLOWS symlinks by default, so a dangling link reads as absent — that is the right answer for "can I open something here" and the wrong one for "is this name taken". exists(path, {follow_symlinks: false}) is the lstat form and sees the link itself (v0.39.0). opts may be nil, like being omitted.
access Ask the kernel whether this process can access path using its effective uid/gid: mode is a non-empty, duplicate-free string of r/w/x/f letters (f = existence and is redundant when combined). Follows symlinks. Unlike inspecting stat().perm, this honours POSIX ACLs. Ordinary absence/denial returns false; malformed input or an unexpected syscall failure raises (v0.45.0).
is_dir Test if path is a directory
is_file Test if path is a regular file
realpath Canonicalise a path: resolve every symlink + ./.. to the absolute real path (like readlink -f / realpath(3)). The path MUST exist. realpath(path) -> string | nil (nil ONLY when a component is missing; a permission/IO/symlink-loop/non-UTF-8 failure RAISES — v0.103.14). NORMALISATION ONLY, not a race-free authorization primitive: canonicalise-then-use is not atomic, so for an exec/open safety check, exec/open the RETURNED canonical path (which has no symlinks to re-traverse), not the original (v0.31.2)
glob List files matching a glob pattern (supports ** globstar in v0.2.1)
ls List directory entries
mkdir Create directory: mkdir(path[, {parents}]). parents defaults to true (create_dir_all). {parents: false} creates only the final component and fails if the parent is missing — the form to use when the parent was placed deliberately and re-creating it would hide its removal (v0.42.0)
flock Take a process-held advisory file lock: flock(path[, {shared, wait, create}]) -> bool. Exclusive and non-blocking by default; contention returns false, genuine filesystem errors raise. wait is seconds (0 = do not wait). A shared lock opens an existing file read-only, so it needs no write access; an exclusive one opens read-write. A missing file is created (0644) unless create:false, which raises LOCK_FILE_MISSING (v0.117.0). Repeated acquisition of the same canonical path by this process is idempotent-true (v0.43.0)
funlock Release and close this process's advisory lock for path. Returns true when held, false when not held (v0.43.0)
fcntl_lock Take a POSIX record lock over the whole file: fcntl_lock(path[, {shared, wait, create}]) -> bool — the fcntl(2) lock namespace C daemons use for pidfiles/lockfiles, which flock(2) locks never see (Linux keeps the two families separate). Contract-identical to flock: exclusive + non-blocking default, contention false, real errors raise, shared opens an existing file read-only, create:false refuses a missing one, per-process idempotent-true by canonical path. Implemented as open-file-description locks (F_OFD_SETLK): they conflict with other processes' traditional fcntl records, and survive this process opening/closing the same path elsewhere — a read_file of the locked path cannot drop them (v0.71.0)
fcntl_unlock Release and close this process's POSIX record lock for path. true when held, false when not (v0.71.0)
copy Copy a single file: copy(src, dst). Overwrites dst; preserves the source permission bits. Use copy_tree for a directory (v0.22.0).
copy_tree Recursively copy a directory: copy_tree(src, dst). Creates dst, copies files (perms preserved) and symlinks (as symlinks); merges into an existing dst (v0.22.0).
symlink Create a symbolic link: symlink(target, linkpath) — symlink(2), arguments in symlink(2) order (target first, the link to create second). target is stored verbatim and is NOT resolved or validated: a relative target resolves against the link's own directory, and creating a dangling link is legal. Raises EEXIST if linkpath already exists. Read the other way with read_link() (v0.38.0).
read_link Read a symbolic link's target: read_link(path) -> string — readlink(2), returning the target EXACTLY as stored (possibly relative, possibly dangling), which is what distinguishes it from realpath()'s full resolution. Raises EINVAL when path is not a symlink; test first with stat(path, {follow_symlinks: false}).is_symlink (v0.38.0).
rename Rename/move a path within one filesystem: rename(src, dst) — rename(2), so replacing an existing dst is ATOMIC (a concurrent reader sees either the old file or the new one, never a partial write). This is the primitive for a safe in-place update: write a temp file beside the target, then rename over it. Raises EXDEV across filesystems (copy + remove instead) and ENOENT when src is missing (v0.37.0).
remove Remove a single file/symlink: remove(path). No-op if already gone (rm -f). Errors if path is a directory — use remove_dir (v0.22.0).
remove_dir Recursively remove a directory and its contents: remove_dir(path) (rm -rf). No-op if already gone (v0.22.0).
chmod Set file/directory permissions. chmod(path, 0o755) — mode as a VALUE (use an octal literal) or an octal string "0755" (v0.11.0: a number is now the value, not its decimal digits read as octal)
fsync Make a file, a directory or a whole tree durable: fsync(path[, {recursive}]) -> number of inodes synced. A file is fsync(2)ed; a directory is opened and fsynced, which persists the entries created, renamed or removed in it; recursive:true syncs every regular file and directory beneath (children first, symlinks never followed, their entries persisted by their directory), then the directory itself. The path itself follows a symlink. Failures raise FSYNC_FAILED with details.path naming what could not be synced (v0.117.0)
chown Set file owner/group by numeric uid/gid: chown(path, 1000, 1000). Follows symlinks by default; chown(path, uid, gid, {follow_symlinks:false}) changes a symlink itself (lchown, v0.117.0). Numeric only (no name resolution) (v0.17.1)
mime_type The type of a file from the freedesktop shared-mime-info database, as the desktop names it (requires mime feature): mime_type(path[, {content}]) -> {mime, by, uncertain, exists, parents}. Globs first, magic to settle a missing or ambiguous glob, then a text check (the GLib order). by is stat|glob|magic|content|fallback; parents lists every supertype nearest first, ending text/plain (for text/) and application/octet-stream. {content:false} never opens the file. A missing path is typed by name with exists:false; an unreadable file by name, uncertain. Follows symlinks (v0.113.0)
stat Stat a path → map {uid, gid, nlink, size, mode, perm, ino, dev, ctime, mtime, atime, ctime_nsec, mtime_nsec, atime_nsec, is_file, is_dir, is_symlink}. ino/dev are STRINGS (u64, exceed f64 exact range); mode is full st_mode, perm = mode & 0o7777; time are epoch seconds (f64) and _nsec the sub-second part 0..=999999999 (v0.44.0) — compare the PAIR to see a same-second rewrite. Follows symlinks by default; stat(path, {follow_symlinks:false}) is lstat (v0.17.1)
line_count Count lines in a file by streaming — never loads the whole file (byte-oriented, so it works on non-UTF-8 files too) (streams since v0.28.1)
head First N lines of a file as a list (default 10) — streams and stops after N lines, never reads the rest (the no-slurp twin of take(read_lines(p), n)) (v0.28.1)
tail Last N lines of a file as a list (default 10) — reads backwards in blocks from EOF, never slurps the whole file (the no-slurp twin of take(read_lines(p), -n)) (v0.28.1)
basename Return the filename component of a path
dirname Return the directory component of a path
extname Return the file extension (including the leading dot)
path_join Join path components with the native separator
path_parts Decompose a path into {dir, base, stem, ext} (v0.2.1)
walk Recursive directory walk: walk(dir, {max_depth, follow_symlinks, include_dirs, strict}); invalid max_depth raises instead of becoming unlimited (strict since v0.55.0). Unreadable directories and unstat-able entries are skipped unless strict: true, which raises WALK_UNREADABLE (details.path names the first one) so a gate can tell no matches from could not look
readline Read a line from stdin (optional prompt argument)
read_stdin Read all of stdin to EOF as a string (for pipe/hook input). STRICT UTF-8 — binary stdin raises; use read_stdin_bytes for that
read_stdin_bytes Read all of stdin to EOF as raw bytes — the binary twin of read_stdin, which refuses invalid UTF-8. Optional arg caps the read: read_stdin_bytes(8192) reads at most 8192 bytes, the same cap contract as read_file_bytes (v0.65.0)
tty_mode Put the controlling terminal into raw or cooked mode: tty_mode("raw") disables canonical buffering, echo and signal generation (a lone ESC arrives as one byte — the keystroke-recorder primitive), tty_mode("cooked") restores the saved termios. The original termios is restored on process exit if the caller never returns to cooked (v0.103.9)
stdin_copy Copy stdin to a file, flushing every chunk, until EOF — the incremental twin of read_stdin_bytes, so a recorder killed mid-stream keeps the bytes it already read. Pair with tty_mode("raw") to capture keystrokes (v0.103.9)
sqlopen Open a SQLite database and return a handle: sqlopen(path[, mode]). mode "ro" (default) is read-only and the file must exist (SQL_NO_DATABASE otherwise); "rw" or "rwc" opens read-write and creates a missing file, in WAL mode with a 5 s busy timeout. ":memory:" is a private in-memory database. Any other mode raises. See mix man sqlopen
tar_list List a tar archive (zstd default, gzip or none codec) WITHOUT extracting: streaming entries {name,size,mode,uid,gid,kind,mtime}; the compressed stream is drained to EOF so the frame checksum is verified
tar_unpack Safely unpack a tar archive into dest (which must not exist, in a parent no other user can write unless sticky): every member is validated (no absolute/.. paths, a leading ./ accepted, no device/fifo, symlink targets contained relative to the link's own directory and never through another symlink, no extraction through a created symlink, hardlinks only to earlier files), entries land in a private staging dir held by descriptor and renamed into place (never replacing dest) only after the stream verifies to EOF — non-zero data after the tar end is refused (one tar; one zstd frame, gzip members concatenate); PAX global headers count as metadata. opts: codec (zstd default), numeric_owner (default true for root only), xattrs (default true: user. only), keep_special_bits (default false strips suid/sgid and security.capability), privileged_xattrs (default false: trusted., ACLs, other security.), max_entries, max_bytes, max_name, max_stream_bytes (bounds the decoded stream, longname/PAX buffers included), max_xattr_bytes (16 MiB). Unapplied owners are listed in the receipt's warnings, and their special bits dropped
tar_pack Pack a source DIRECTORY into a new archive at path (deterministic sorted walk, numeric owner/mtime/mode, security.capability captured as a SCHILY.xattr PAX record with keep_special_bits; refuses device/fifo members, an output inside the source, and a file that changes size while packed). opts: codec (zstd default), level (zstd 1..=22 default 7, gzip clamps to 9), keep_special_bits (default false strips suid/sgid)
sqlexec Execute SQL on a SQLite handle, return result rows
sqlclose Close a SQLite database handle
write_stdout Write values to fd 1 AS THEY ARE — no trailing newline, no separator, flushed. bytes/buffer go out verbatim; every other value renders exactly as print() renders it. Never re-opens a path, so it works when fd 1 belongs to another user (the sieve-filter case). A failed write RAISES (catchable): IO_BROKEN_PIPE when the consumer went away, else IO_WRITE_FAILED — print raises the same way since 0.99.0 (B9)
write_stderr Write values to fd 2 as they are — the stderr twin of write_stdout, same contract (v0.65.0)
print_raw ALIAS of write_stdout — identical contract, the spelling for print without the trailing newline (v0.65.0)
eprint_raw ALIAS of write_stderr — identical contract, the spelling for eprint without the trailing newline (v0.65.0)
print Print values to stdout with newline (statement, not a builtin call); bare print emits a blank line
eprint Print values to stderr with newline (statement, not a builtin call); bare eprint emits a blank line
system — see system
env Get environment variable value ("" if unset); env(name, default) returns default when unset or empty
time Return current Unix timestamp as float
monotonic Return CLOCK_MONOTONIC seconds as a float — the kernel monotonic clock (the one compositor FRAME_TRACE start_us/end_us stamp), never stepped by NTP or wall-clock adjustment, so it never goes backwards and differences are true elapsed intervals. The epoch is unspecified (on Linux it counts from boot); use time() for calendar timestamps, this for measuring and for correlating with compositor traces
pid Return current process ID
uid Effective user id of this process (geteuid) — normally the id a file access is checked against (Linux checks fsuid, which tracks euid unless setfsuid(2) is called; no Mix script can call it, though an embedder can), so it is the one to compare a stat() map's uid against when deciding whether a path is yours (v0.41.0)
gid Effective group id of this process (getegid) — the companion to uid(), for comparing against a stat() map's gid; same fsgid caveat, and it answers only whether a file's group is the EFFECTIVE one, so use groups() to decide which permission class applies (v0.41.0)
groups Every group id this process is in — the getgroups(2) supplementary set plus the effective gid, sorted. This is what decides whether the kernel applies a file's GROUP or OTHER permission bits: gid() alone cannot answer it for a file grouped under one of your other groups (v0.42.0)
args Return list of script arguments
getopt Parse args against a spec map: getopt(args(), {all:{short:"a"}, out:{short:"o", arg:true}}) -> {opts, rest, errors}. opts has every declared option (flag->bool, value->string|nil); rest=positionals (incl. post --); errors=collected unknown-option/missing-value strings ([]=clean). Forms: --long, -s, --k=v, --k v, -s v, -- terminator. Minimal: no bundling/abbrev (v0.12.0)
exit Exit with optional status code
sleep Sleep for N seconds (async)
task_start Start an in-process handler task; command must have an async handler. No Bus traffic. Requires the runner's LocalSet; drained on shutdown
run Run shell command via sh, return trimmed stdout as string. run(cmd, [{timeout: seconds}]) — 0 (default) = no deadline; a timed-out child is PG-killed and run dies (catchable)
run_rc Run shell command, return {rc, stdout, stderr, timed_out, interrupted} map. run_rc(cmd, [{timeout: seconds}]) — 0 (default) = no deadline; timeout → rc=-1 timed_out=true
run_stream Run an argv LIST directly (no sh), inheriting stdio so output streams live and the child can use the terminal (interactive when it has a pty, e.g. ssh -t); returns the exit code. run_stream(argv, [{env, clear_env, cwd}]) — same env/cwd semantics as run_argv, so an interactive child gets variables without an env prefix exposing them in its ps argv (v0.51.0). The run_argv-only opts (timeout, stdin, stdout, stderr, max_output, stream) are rejected by name: this runner blocks until the child exits and captures nothing
run_argv Run an argv list directly (no shell) with structured stdio routing and a whole-call deadline that starts before route opening. opts: timeout; grace (seconds: at the deadline SIGTERM the process group, wait up to grace for it, then SIGKILL — default 0 = SIGKILL at once; refused with timeout:0); stdin nil|string|bytes|buffer|{file}|{null:true}|{inherit:true} (only when mix's own stdin is not a terminal, else STDIN_TERMINAL — use run_stream); stdout capture|inherit|null|{file,append?,mode?}; stderr capture|inherit|null|stdout|{file,append?,mode?}; cwd/env/clear_env; max_output; stream. stdout/stderr default capture; output files default truncate, mode 0o600. Routed non-capture streams return "" with truncation false and are not capped. stderr:stdout merges into stdout. File-open failure or route-open deadline is a PROCESS_STDIO value and the child is not spawned. Captured output abandoned at a deadline is returned partially with its truncation flag true. stream:true + stdout:inherit and all bad options raise OPTION_INVALID before spawn. opts may be nil (defaults). Ordinary command/setup failure is encoded in the VALUE; timeout default 30s; max_output default 8 MiB per captured stream
run_parallel Run many argv jobs concurrently with a bounded worker pool: run_parallel(jobs[, {max, timeout}]) -> list of process_result maps in INPUT order. Each job is an argv list (like run_argv's first arg) OR a {argv, stdin, cwd, env, clear_env, stdout, stderr, max_output, timeout, grace} map mirroring run_argv's options. max bounds concurrency (default 8, hard-capped at 256 live workers — excess jobs still run, drained by index); a top-level timeout (seconds) overrides every job's own. A job may NOT disable its deadline (timeout: 0 is refused) — one hung job would park the whole batch. Each result is EXACTLY run_argv's process_result map, so existing result-handling code ports unchanged; one job's ordinary failure (nonzero/timeout/spawn) is DATA in its map, never a raise. Process-level fan-out (std::thread over the run_argv engine), NOT in-language concurrency — the evaluator is single-threaded and Values never cross a thread; a job's stream flag is ignored (parallel tee would interleave). The killer use is ssh_mix fan-out: run_parallel of ssh argvs. A parse error in ANY job fails the whole call before spawning (v0.82.0)
run_argv_must Fail-fast run_argv with the same structured stdio opts: returns captured stdout unchanged when ok and no captured stream truncated ("" when stdout is routed), else raises PROCESS_EXIT_NONZERO / PROCESS_TIMEOUT / PROCESS_SIGNAL / PROCESS_INTERRUPTED / PROCESS_OUTPUT_LIMIT or the result's setup/lifecycle error_code (PROCESS_STDIO / PROCESS_SPAWN / PROCESS_IO / PROCESS_INTERNAL) with the complete result map in $err.details.result
run_pipeline Run one or more argv stages without a shell, connecting each stdout to the next stdin. Stage maps accept argv/cwd/env/clear_env/stderr, plus stdin on the first stage and stdout on the last, using run_argv's stdio grammar. Every route and pipe is prepared before any stage runs, so PIPELINE_STDIO means no stage ran. Returns a distinct pipeline_result with final stdout/exit fields and per-stage outcomes. One whole-call deadline starts before route opening; captured output abandoned at that deadline is partial with its truncation flag true. Non-final SIGPIPE is NOT accepted by default: any stage killed by a signal makes the pipeline not-ok, matching set -o pipefail. Pass allow_signal:true to accept a non-final SIGPIPE when every downstream stage succeeded (the yes | head -1 idiom). Every stage carries status ok|exit_nonzero|signal|broken_pipe|timeout|interrupted|setup_error and broken_pipe (killed by SIGPIPE: its reader closed); the result carries status ok|exit_nonzero|signal|broken_pipe|timeout|interrupted|setup_error, failed_stage (the RIGHTMOST non-ok stage, pipefail's rule; nil when none) and a one-line human summary — gates branch on status, never on text. Ordinary failure is encoded in the VALUE — never raises
run_pipeline_must Fail-fast run_pipeline twin: returns final stdout unchanged when the pipeline is ok and no captured output truncated; otherwise raises PIPELINE_* with the complete pipeline_result in $err.details.result
spawn Start a child and return its PID. String form uses a shell and optional stdout/stderr paths. Argv form runs directly with options: detach, die_with_parent, exit_event, tag, cwd, env, clear_env, stdout, stderr. exit_event:true (Linux pidfd) registers the sole reaper in this evaluator and emits proc.exited {pid,tag,exit_code,signal}; tag is a string up to 4096 bytes, default empty. Cannot detach a managed child. Managed children are killed and reaped when their evaluator retires, after handler drain; failed reload leaves old managed children intact. die_with_parent additionally arms kernel parent-death SIGKILL. Without exit_event, existing detached/owned/plain spawn semantics apply. Stdio defaults null, capture is refused; cwd/env/clear_env match run_argv. See system manual for lifetime rules
kill Send signal to process (default SIGTERM); returns false when the signal could not be delivered. Both arguments must be whole NUMBERS and neither is coerced — a bool/string pid raises TYPE_MISMATCH rather than becoming 0 (which signals this process's whole group), and an unrecognised signal raises rather than silently defaulting to SIGTERM (strict since v0.52.0)
shell_quote Single-quote-wrap a string for safe interpolation into a POSIX shell command
sql_quote Escape a string for SQL string literals: doubles ' and escapes \ (MySQL/MariaDB-safe — the documented target; also safe for SQLite, where a literal backslash arrives doubled — use sqlexec binds for exact bytes); NUL bytes stripped
random_password Generate an alphanumeric password (default len 16, no O/o, guaranteed upper+lower+digit)
ssh_run Run a command on a remote host via ssh; returns {stdout, stderr, exit_code, ok, duration_ms, host, timed_out, interrupted, utf8_lossy, stdout_truncated, stderr_truncated}; max_output optionally caps local capture bytes per stream (omitted: unbounded; 0 is REJECTED — unlike run_argv, where 0 means unbounded — omit the key instead). A cap that cuts mid-UTF-8-sequence makes the lossy decode insert U+FFFD, so utf8_lossy: true beside stdout_truncated: true may be local truncation damage, not remote garbage. env values ship by default in a Mix driver to the remote mix binary (mix_bin opt, else local env MIX_REMOTE_BIN, else /opt/mixos/bin/mix)
ssh_must ssh_run wrapper: returns stdout on success, throws a Mix error otherwise
ssh_mix Run Mix source on a remote host: ships the source over ssh stdin into <mix_bin> -, bypassing ALL shell quoting. The remote binary is the mix_bin opt, else the local env var MIX_REMOTE_BIN, else /opt/mixos/bin/mix; it must be an absolute path or a bare name using only [A-Za-z0-9_./+-] (anything else raises OPTION_INVALID locally). ssh_mix(host, source, [opts]) -> same map as ssh_run; bindings maps valid Mix identifier names to strict-data-encoded values prepended as $name assignments, and decode:"data"|"json" adds a parsed .value from stdout. max_output caps local capture per stream (0 rejected; omit for unbounded); a truncated stdout REFUSES to decode (raises) — a truncated prefix can parse as a smaller, wrong value — so omit decode and inspect stdout/stdout_truncated to work with partial output. Accepts every ssh_run opt except stdin/env_transport. Remote command failure stays in the result value; invalid arguments/options raise locally. (v0.20.4)
ssh_mix_many ssh_mix on many hosts at once: ssh_mix_many(hosts, source[, opts]) -> map host -> ssh_result, keyed in INPUT order. Every host gets the same source, bindings, env and decode, and each result is EXACTLY ssh_mix's ssh_result map, so per-host handling code ports unchanged. opts = every ssh_mix opt plus max (concurrency, default 8, at most 256 live workers — run_parallel's numbers, its own loop). Options are validated once (even for an empty host list) and every host is checked before any ssh spawns; a wrong argument type raises TYPE_MISMATCH. After that nothing raises — one host's failure is DATA in its own map: unreachable, nonzero exit and timeout arrive as ok:false; a local spawn/pipe failure as ok:false with error_code (PROCESS_SPAWN, PROCESS_STDIO, …) and error; a decode refusal (truncated or unparseable stdout, where ssh_mix raises) as ok:false, decode_error and no value; a host never started because of Ctrl-C as ok:false, interrupted:true. hosts must be unique strings; timeout:0 is refused (one hung host would park the batch); there is no whole-call deadline
send_mail Send a plain-text email through the local MTA: send_mail({to, from, subject, body[, headers]}[, {host, sendmail, timeout, mix_bin}]) -> run_argv's process_result + message_id (+ host, only with the host opt). Renders an RFC 5322 message (From, To, Subject, Date, Message-ID, MIME-Version, text/plain utf-8) and pipes it to sendmail -t -i -f <envelope> on stdin — never a network SMTP client; ok means the MTA accepted it, not that it was delivered. to is a string or list of strings; Cc/Bcc go in headers. from is exactly one mailbox, addr or Name <addr> (a comment, a second mailbox or <> raises); its address is the envelope sender. Long headers are folded within 78 columns (an unbreakable run over 998 raises); a non-ASCII body or one with a line over 998 bytes goes quoted-printable, else 7bit. A non-ASCII subject is RFC 2047-encoded; every other header value must be ASCII, and CR/LF/NUL in any header raises. headers adds headers or replaces the generated Date/Message-ID/MIME-Version; From/To/Subject there raise (set them in msg), and so do Content-Type/Content-Transfer-Encoding (send_mail encodes the body itself, always text/plain utf-8). sendmail is found on PATH, then /usr/sbin/sendmail, /usr/lib/sendmail, unless the sendmail opt names it. host sends from that host via ssh_exec (remote default /usr/sbin/sendmail); mix_bin (host only) names the remote mix binary, as for ssh_mix. An MTA failure or missing sendmail is DATA (ok:false, exit_code, stderr, error_code); bad input raises before anything runs: OPTION_INVALID (msg fields, options), TYPE_MISMATCH (msg not a map, arity), or ssh_exec's own validation errors with host
ssh_exec Run an argv list DIRECTLY on a remote host via a strict-data driver and remote run_argv. Remote stdio allowlist: stdin nil|string|{file}|{null:true} (a stdin STRING is always data, as locally — there is no stdin "inherit" route on either side); stdout capture|null|{file}; stderr capture|null|stdout|{file}. File paths resolve remotely. stdout/stderr inherit and stream:true raise OPTION_INVALID locally before ssh because they would corrupt or bypass the result envelope. Binary stdin also raises locally. Transport/protocol failures and remote command failure are returned in the process_result plus host; a remote without run_argv returns SSH_REMOTE_UNSUPPORTED without running the command. The remote mix binary is the mix_bin opt (older name remote_mix), else local env MIX_REMOTE_BIN, else /opt/mixos/bin/mix
process_alive Test if a process exists (signal 0 check). EPERM counts as alive: existence does not imply permission to signal, including another user's process. pid must be a positive whole NUMBER; no coercion. Nonpositive, bool or string PIDs raise TYPE_MISMATCH. Reaps exited unmanaged children; controller-owned job and registered native-task PIDs use only signal 0 so their sole wait owner retains every status (zombies may briefly report alive).
net_watch Subscribe to Linux rtnetlink link/address changes; returns an evaluator-owned opaque handle. Delivers net.changed {watch, changes:[{kind:"link"|"addr", ifname, index, up, removed, operstate?, loopback?, wireless?, family?, address?, prefix?}], overflow, closed?} via $event.args; bursts coalesce per link/address, repeats that change nothing are dropped, overflow means re-read net_state(). opts {events: ["link","addr"]}. No polling fallback
net_unwatch Cancel a net_watch handle and its pending changes
net_state Snapshot of network links and addresses from an rtnetlink dump: {links:[{ifname, index, up, operstate?, loopback, wireless}], addresses:[{ifname, index, family, address, prefix}]}. up = operstate up, or IFF_UP+IFF_RUNNING when the driver reports unknown (lo, WireGuard)
audio_watch Subscribe to PipeWire (pulse server) change notices through one managed pactl subscribe child; returns an evaluator-owned opaque handle. Delivers audio.changed {watch, changes:[{facility, kind, index}], overflow, closed?} via $event.args; read the volume with audio_state() once per batch. closed {error_code:AUDIO_SOURCE_EXITED} when the server goes away. opts {facilities (default sink, source, server, card), runtime_dir}
audio_unwatch Cancel an audio_watch handle: kills and reaps its pactl child, drops pending changes
audio_state Default audio sink volume via one wpctl get-volume @DEFAULT_AUDIO_SINK@ (2 s deadline): {ok, volume, level (0-100+), muted, reason?}. No default sink, a wpctl that is missing or cannot start, or a timeout is ok:false with a reason, never an error. opts {runtime_dir}
panic Abort via an uncatchable Rust panic (distinct from catchable die); the SPEC 18 §3.4 handler boundary isolates it in --serve mode
raise Raise a catchable structured error: raise(code, message[, details]) — code is UPPER_SNAKE (e.g. "VALIDATION_REQUIRED", stable identifiers, scripts may define their own); a non-string message is coerced to its string form; catch with catch $msg, $err and read $err.code / $err.details / $err.frames (v0.29.0)
hostname Return the system hostname
cwd Return current working directory
chdir Change current working directory
platform Return OS platform string (linux, macos, windows, etc.)
script_path The absolute, symlink-resolved path of the running ENTRY script, fixed when it starts: the same answer as realpath($0) at start, and a chdir() does not move it. nil under mix -c, in the REPL and for mix - (stdin). Always the entry script: a require()d module, even during its top-level init, gets the entry script's path, not its own (v0.111.0)
script_version The running entry script's provenance: {name, version, sha, sha256, modified, mix: {version, sha, dirty}} — version is the -- version: X.Y.Z header (nil when absent or malformed), sha the first 12 hex of the content SHA-256, modified the file mtime as RFC 3339 UTC (nil for mix -). Always the ENTRY script (a require()d module gets its caller's record); held per evaluator, so each --serve RELOAD generation answers for its own file. Returns nil in the REPL, under mix -c, and in embedders that install no record. The same facts mix SCRIPT --version prints, as data (v0.95.0)
which Locate an EXECUTABLE in PATH: a PATH entry is returned only if it is a regular file the kernel says this process may execute (faccessat2 X_OK, so POSIX ACLs count), never merely a file that exists, and never a directory. cmd must be a string and is not coerced. Returns nil when nothing on PATH is runnable under that name (executability enforced since v0.52.0)
date_format Format Unix timestamp with strftime pattern
date_parse Parse date string with strftime pattern into Unix timestamp
now_iso Current time as ISO 8601 string
duration_format Format seconds as human-readable duration (e.g. "2h 15m")
relative_time Format timestamp as relative string (e.g. "3 hours ago")
base64_encode Encode string as base64
base64_decode Decode base64 string
hash_blake3 BLAKE3 hash of a string/bytes/buffer → lowercase hex; pass {raw:true} for the raw digest as bytes (v0.66.0)
hash_sha256 SHA-256 hash of a string/bytes/buffer → lowercase hex; pass {raw:true} for the raw 32 digest bytes (v0.66.0 — before that a second argument was silently IGNORED)
hash_md5 MD5 hash of a string/bytes/buffer → lowercase hex; {raw:true} → bytes. ⚠ CRYPTOGRAPHICALLY BROKEN (collisions since 2004) — legacy interop only (Content-MD5, mail dedup keys, checksums against existing tools), NEVER a security decision; use hash_sha256/hash_blake3 for those (v0.66.0)
hash_sha1 SHA-1 hash of a string/bytes/buffer → lowercase hex; {raw:true} → bytes. ⚠ CRYPTOGRAPHICALLY BROKEN (SHAttered, 2017) — legacy interop only (git object ids, older ETags/APIs), NEVER a security decision; use hash_sha256/hash_blake3 for those (v0.66.0)
hmac_sha256 HMAC-SHA256 (RFC 2104) of a message with a secret key → lowercase hex; {raw:true} → the 32 MAC bytes (v0.66.0) — webhook signature verification (Stripe-Signature etc). Accepts string/bytes/buffer for both args (requires crypto feature)
password_hash Hash a password: password_hash(plaintext[, cost]) → bcrypt $2b$… string (cost 4-31, default 12; input over 72 bytes raises), or password_hash(plaintext, {scheme: "sha512-crypt"[, rounds]}) → $6$… for Dovecot passdbs (rounds 1000-999999999, default 5000; 16-char salt; default rounds emit the implicit glibc $6$salt$hash form, 120 chars with the {SHA512-CRYPT} prefix, while any other rounds value adds a rounds=N$ field). Client-side hashing before writing maild.accounts.password over the Bus — a raw props.set stores the field verbatim (requires crypto feature; v0.71.0, sha512-crypt v0.102.6)
password_verify Check a plaintext password against a hash: bcrypt (any $2a$/$2b$/$2y$ form) or SHA-crypt ($6$/$5$, incl. the Dovecot {SHA512-CRYPT} prefix) → bool. A malformed hash RAISES rather than answering false — a corrupt stored hash is a config fault, not a wrong password (requires crypto feature; v0.71.0, sha-crypt v0.102.6)
constant_time_eq Timing-safe equality for secrets/MACs: compares full length with no early exit (plain == leaks a timing oracle). Use for webhook signature comparison. Accepts string/bytes/buffer
jwt_rs256_sign Sign JSON object claims with a PEM RSA private key using RS256; optional JSON object headers, alg pinned to RS256. Returns a compact JWT; crypto feature. No token exchange or expiry/audience policy
hash_file Streaming digest of a file, fixed 64 KiB working set whatever the size: hash_file(path[, "md5"|"sha1"|"sha256"|"blake3"|nil][, {raw:true}]) → lowercase hex, or bytes with {raw:true}. md5/sha1 added v0.66.0 and are BROKEN hashes for legacy interop only (v0.24.0)
uuid Generate a new random UUID v4 string
dkim_keygen Generate a DKIM keypair. dkim_keygen("rsa", [bits=2048]) or dkim_keygen("ed25519") → {algorithm, private_pem, public_b64, dns_txt_record}
http_get HTTP GET. http_get(url, [headers], [{timeout, ssl_verify, ca_file, ca_pem}] — timeout default 30, 0 disables; ssl_verify default true, false skips TLS cert/hostname checks like curl -k; ca_file/ca_pem ADD a private CA to the default roots — mutually exclusive with each other and with ssl_verify:false, 4 MiB cap, bad PEM raises HTTP_TLS, v0.29.0) → {status, body, bytes, headers, final_url, duration_ms, error_code, error} (headers lowercase-name→list; final_url after redirects; transport failure = status:0 + HTTP_* error_code; v0.30.0). body is the response decoded as UTF-8 (nil if not valid UTF-8); bytes is the raw byte buffer. Response bodies are capped at 64 MiB (over-cap → {status:0, error}).
http_post HTTP POST. http_post(url, body, [headers], [{timeout, ssl_verify, ca_file, ca_pem}]) → {status, body, bytes, headers, final_url, duration_ms, error_code, error} (headers lowercase-name→list; final_url after redirects; transport failure = status:0 + HTTP_* error_code; v0.30.0). Opts (incl. ssl_verify: false → skip TLS verification like curl -k) and body/bytes semantics (incl. the 64 MiB body cap) match http_get.
http_request HTTP any-verb. http_request(method, url, [body], [headers], [{timeout, ssl_verify, ca_file, ca_pem}]) → {status, body, bytes, headers, final_url, duration_ms, error_code, error} (headers lowercase-name→list; final_url after redirects; transport failure = status:0 + HTTP_* error_code; v0.30.0). Opts (incl. ssl_verify: false → skip TLS verification like curl -k) and body/bytes semantics (incl. the 64 MiB body cap) match http_get.
http_post_multipart HTTP POST with a multipart/form-data body: http_post_multipart(url, fields, files, [headers], [opts]) → http_response map. fields is a map of string form fields; files is a map of field_name → {filename, content_type?, data: bytes|buffer} (raw binary payloads, the webhook/file-upload slot — v0.103.12). The boundary is generated, and the Content-Type header is set accordingly.
http_put_file Stream a regular file or inclusive range with exact Content-Length. Opts: method PUT/POST/PATCH (default PUT), range:{start,end}, headers, idle_timeout (seconds, default 30), deadline (seconds, default 0), ssl_verify, ca_file, ca_pem. Redirects disabled; 3xx returned. Deadline is cooperative and best-effort during blocking DNS/IO. Fixed 64 KiB transfer buffer; response capped at 64 MiB. Operational errors return status:0; invalid options raise. bytes_written counts source bytes consumed, size is window size, blake3 hashes that window (nil on failure). published is false (no local publication).
http_get_file Stream GET through a unique temp sibling and publish atomically. Opts: overwrite (default false), append (default false; requires expect_blake3 and matching 206 Content-Range), expect_blake3 (64 lowercase hex), max_bytes (final size), headers, idle_timeout (seconds, default 30), deadline (seconds, default 0), ssl_verify, ca_file, ca_pem. 200/206 require identity framing and Content-Length; missing length or encoded responses return HTTP_IDENTITY_FRAMING. Append copies and hashes the existing prefix. Redirects returned; error bodies never installed. No-replace is atomic. Deadline is cooperative and best-effort during blocking DNS/IO. bytes_written counts downloaded bytes, size includes prefix, blake3 hashes the final file. published records publication even if directory fsync fails. Operational errors return status:0; invalid options raise.
bytes_len Length of a Value::Bytes buffer in bytes (v0.3.1)
string_to_bytes Convert a string to its UTF-8 byte representation (v0.3.1)
bytes_to_string Convert a bytes buffer to a string; strict UTF-8, or pass {lossy:true} for a from_utf8_lossy decode (v0.17.2). Also accepts a Buffer.
bytes_find 0-based byte offset of needle in a bytes/buffer value, -1 if absent; optional from (signed, clamped) starts the scan and the result stays ABSOLUTE. Needle: bytes/buffer/string/byte number 0-255, and must not be empty. ⚠ NEVER use bare in a condition: -1 is truthy, 0 is falsy (MIX-W2305) (v0.64.0)
bytes_starts_with Test whether a bytes/buffer value starts with a prefix (bytes/buffer/string/byte number 0-255); an empty prefix is true (v0.64.0)
bytes_ends_with Test whether a bytes/buffer value ends with a suffix (bytes/buffer/string/byte number 0-255); an empty suffix is true (v0.70.0)
bytes_split Split a bytes/buffer value on a separator (bytes/buffer/string/byte number 0-255) → list of bytes. Same piece rules as split(): absent separator → one whole piece, leading/trailing separator → empty piece. An EMPTY separator raises (v0.64.0)
bytes_concat Concatenate 1+ bytes/buffer/string values into one new bytes (a string joins as its own UTF-8). A list argument raises and names bytes_from (v0.64.0)
bytes_from Build bytes from a LIST, flat-splicing each item — int 0-255 = one byte, string = its UTF-8, bytes/buffer = its content (same item vocabulary as buffer([items])) (v0.64.0)
bytes_to_hex Lowercase hex of a bytes/buffer value, two chars per byte, no separator (v0.64.0)
bytes_from_hex Decode a hex string to bytes — strict: even length, [0-9a-fA-F] only, no separators or whitespace. Exact inverse of bytes_to_hex (v0.64.0)
dns_query Ask one DNS server one question and get the whole answer: dns_query(name, type[, {server, port, timeout, tcp, recurse}]) -> {ok, rcode, aa, ra, tc, answers, authority, additional, server, port, transport, duration_ms}; each record is {name, type, ttl, data}. type is A, AAAA, MX, TXT, SRV, NS, SOA, PTR, CNAME, CAA, ... (PTR of an IP address asks its reverse name). server defaults to the first resolv.conf nameserver, port 53, timeout 5 s, recurse true (the RD flag); tcp:true forces TCP and a truncated UDP answer is retried over TCP. The name is always fully qualified (no search list). Timeout, unreachable and malformed replies return {ok:false, error_code: DNS_TIMEOUT|DNS_UNREACHABLE|DNS_PROTOCOL|DNS_IO|DNS_SERVER, error}; bad arguments raise (v0.117.0)
dns_lookup Resolve a hostname to a list of IP address strings
udp_send Send ONE UDP datagram: udp_send(host, port, payload) -> bytes sent. Payload (string/bytes/buffer) goes out verbatim; host is resolved. Not a socket API — nothing to hold or close (v0.71.0)
udp_recv Receive ONE UDP datagram: udp_recv(port[, {timeout, host, max}]) -> {bytes, text, from_host, from_port}, or nil on timeout (an ordinary answer, not a fault). timeout seconds default 30, 0 = wait forever; host = bind address default 0.0.0.0; max caps the read (default 65535 = never truncates; a longer datagram truncates to max, recvfrom(2)'s own contract). text is the payload as UTF-8 or nil — bytes always carries the truth (v0.71.0)
ws_connect Open a websocket (ws:// or wss://): ws_connect(url[, {insecure, headers, timeout}]) -> numeric handle. insecure:true skips TLS cert verification (self-signed device endpoints — the LG SSAP case); headers adds handshake request headers; timeout seconds bounds connect AND handshake (default 30, must be positive). Requires ws feature (v0.74.0)
ws_send Send one websocket frame: text for a string payload, binary for bytes/buffer; flushed before returning. A closed connection raises and retires the handle (v0.74.0)
ws_recv Wait for the next DATA frame: ws_recv(handle[, timeout]) -> string (text frame) | bytes (binary frame) | nil on timeout (poll again). timeout seconds default 30, 0 = wait forever. Ping/pong handled internally. A peer close RAISES (catchable) and retires the handle — closed is not confusable with quiet. In a Class C async body, numeric ws_recv(handle[, timeout]) yields on native readiness; subscribed numeric handles refuse SOCKET_SUBSCRIBED. Source-id recv yields outside serve mode; serve mode refuses SOCKET_RECV_SERVE so the event pump cannot steal frames. nil on timeout keeps the connection usable; a terminal raises SOCKET_CLOSED (v0.74.0)
ws_close Close a websocket handle: true when it was live, false when unknown/already retired (never raises for the not-held case, like funlock) (v0.74.0)
http_serve BLOCKING static file server — the python -m http.server slot: http_serve(root[, {port, host, duration, index, listing, render_md, requests}]) -> requests served. GET/HEAD only (405 otherwise), NO TLS ever and NO dynamic handlers (both are webd's job). port 0 (default) binds ephemeral and PRINTS the URL; host defaults 127.0.0.1 (pass "0.0.0.0" to expose); duration 0 = until SIGINT; listing opts into directory indexes; render_md serves .md as HTML (markdown feature); spa (true=index, or a shell filename) answers an extensionless would-be-404 with that shell so a client-side router boots — for a single-shell SPA; clean_urls serves /foo.html for /foo (GitHub Pages parity — for a pre-rendered page-per-route site), tried before spa. Traversal-proof: every canonicalised path must stay under the canonicalised root (v0.75.0)
http_recv Accept ONE HTTP request, answer it, return it: http_recv(port[, {timeout, host, max, respond}]) -> {method, path, query, headers, body, bytes, from_host, from_port}, or nil on timeout. The OAuth-localhost-redirect / webhook-catch shape. respond: {status, body, content_type, headers} (default 200 "ok"); max caps the request body (default 1 MiB); host defaults 127.0.0.1 (v0.75.0)
tcp_connect Open a raw TCP connection: tcp_connect(host, port[, {timeout, tls, insecure}]) -> numeric handle. tls:true wraps in TLS (ring-pinned, webpki roots); insecure:true skips cert verification. The stream-socket primitive for a line/binary protocol (SMTP/redis/memcached probe, banner grab) UDP/WS/HTTP don't cover (v0.78.0)
tcp_send Send bytes on a TCP handle: tcp_send(h, payload[, {timeout}]) -> bytes sent (string/bytes/buffer, verbatim; flushed). Without timeout each write is bounded by the socket's 30 s write timeout, as before. With {timeout} (seconds, 0 = wait forever) one deadline bounds the WHOLE send however many partial writes it takes; on expiry it raises TCP_SEND_TIMEOUT (details {written, total}) and retires the handle, and bytes already written stay written. The timed wait honours SIGTERM and Ctrl-C. On a tcp_on handle the timeout is the receipt deadline (0 keeps the 30 s bound) (v0.78.0; timeout v0.112.1)
tcp_recv Read available bytes: tcp_recv(h[, {timeout, max}]) -> bytes (buffered bytes first, then one read of at most 256 KiB — poll again for more even when max is larger) | nil on timeout (poll again). Bytes not string — a stream has no frame boundary. A peer close RAISES and retires the handle. timeout default 30 (0=forever), max default 64 KiB. Numeric Class C recv yields on native readiness. Source-id recv yields outside serve; serve mode refuses SOCKET_RECV_SERVE and consumes frames through handlers. nil on timeout keeps the connection usable; terminal raises SOCKET_CLOSED (v0.78.0)
tcp_recv_line Read the next LINE: tcp_recv_line(h[, {timeout, max}]) -> string (LF + one trailing CR stripped) | nil on timeout. Buffers across reads; if max bytes accumulate with no newline the handle RAISES and RETIRES (a peer that never terminates a line — broken framing). For line protocols (SMTP/redis) so a caller need not hand-roll a \r\n scanner. On a unix_connect {frame:"nul"} handle a line is a NUL-terminated frame (no CR stripping). From v0.115.0 a line longer than max raises even when its terminator has arrived. Numeric Class C recv yields on native readiness. A line-mode source-id recv yields outside serve; serve mode refuses SOCKET_RECV_SERVE and uses event handlers. nil on timeout keeps the connection usable; terminal raises SOCKET_CLOSED (v0.78.0)
ws_on Subscribe a ws_connect handle to the event stream: ws_on(handle, command) -> source id (string). The connection MOVES to an evaluator-generation reader thread: ordered frames arrive as command events {watch, frame:{kind:"text"|"binary", data}} (binary data hex-encoded), a peer close as ONE terminal {watch, closed:{reason}} — then the source retires. ws_send on the moved handle routes to the owner and awaits its receipt; numeric ws_recv refuses. Serve handlers receive frames as events. The reader parks in poll(2) when idle (no timer). Overflow hard-closes the socket with exactly one terminal event, never a silent drop. ws_unwatch cancels, joins and emits NO terminal marker (v0.107.0)
ws_unwatch Cancel a ws_on subscription: ws_unwatch(source) -> nil. Cancels and joins the reader, drops queued frames and wakes a parked ws_recv with SOCKET_WATCH_HANDLE. Emits NO terminal event (explicit close — documented). Unknown or retired sources raise (v0.107.0)
tcp_on Subscribe a tcp_connect handle to the event stream: tcp_on(handle, command[, {frame:"line"|"bytes", max}]) -> source id (string). frame defaults to "bytes": ordered raw chunks of at most 64 KiB; frame:"line" splits on LF (one trailing CR stripped) with max capping the line length (default 65536) — a line over max hard-closes with one terminal event. On a unix_connect {frame:"nul"} handle, line frames end at NUL; from v0.115.0 a complete line over max also hard-closes. Same ownership, ordering, overflow and retirement rules as ws_on; tcp_recv consumes a bytes source, tcp_recv_line a line source, and a mismatched verb refuses (SOCKET_KIND). A tcp_listen LISTENER subscribes too (no options): each accepted connection arrives as {watch, accepted:{handle, peer:{host, port}}}, handle a plain connected handle; tcp_accept(source) takes the next one outside serve mode (v0.107.0; listeners v0.112.0)
tcp_unwatch Cancel a tcp_on subscription: tcp_unwatch(source) -> nil. Same contract as ws_unwatch (v0.107.0)
tcp_close Close a TCP handle or listener: true when live, false when unknown/already closed (never raises for that, like funlock) (v0.78.0)
tcp_listen Listen for TCP connections: tcp_listen(host, port[, {backlog}]) -> numeric listener handle (tcp_connect's handle space). host is required: "127.0.0.1" is the normal choice, and a non-loopback address ("0.0.0.0", a LAN IP) must be named explicitly. port 0 binds an ephemeral port (read it with tcp_local_addr); backlog defaults to 128 (1..4096). Owned by the evaluator generation. Raises TCP_LISTEN_ADDR_IN_USE, TCP_LISTEN_PERMISSION, TCP_LISTEN_ADDR_UNAVAILABLE, TCP_LISTEN_ADDRESS, TCP_LISTEN_ARGUMENT, TCP_HANDLE_LIMIT (v0.112.0)
tcp_accept Wait for one connection on a tcp_listen listener: tcp_accept(listener[, {timeout}]) -> numeric connected handle (use with tcp_send/tcp_recv/tcp_recv_line/tcp_on/tcp_close) | nil on timeout. timeout seconds default 30, 0 = wait forever. Numeric Class C accept yields on native readiness; a subscribed listener refuses SOCKET_SUBSCRIBED. Source-id accept (a tcp_on listener source) yields outside serve; serve mode refuses SOCKET_RECV_SERVE. Accepted handles belong to the evaluator generation (v0.112.0)
tcp_local_addr Local address of a TCP listener or connected handle: tcp_local_addr(handle) -> {host, port} — how a script that bound port 0 learns its port (v0.112.0)
unix_connect Open a unix-domain stream socket: unix_connect(path[, {timeout, frame}]) -> numeric handle in tcp_connect's handle space, so tcp_send, tcp_recv, tcp_recv_line, tcp_on and tcp_close take it (tcp_local_addr refuses: no TCP address). path is an absolute filesystem path (at most 107 bytes; abstract sockets are not offered). timeout (seconds, default 30) bounds the connect and each write; a listener with a full backlog fails at once (UNIX_CONNECT_BUSY) rather than block. frame:"nul" makes a line a NUL-terminated frame (Varlink, hostd): tcp_recv_line returns the next frame without its NUL and tcp_on {frame:"line"} delivers one per event; send the NUL yourself (tcp_send(h, json .. chr(0))). frame:"line" (default) keeps LF lines. Raises UNIX_CONNECT_NOT_FOUND, UNIX_CONNECT_PERMISSION, UNIX_CONNECT_REFUSED, UNIX_CONNECT_BUSY, UNIX_CONNECT_TIMEOUT, UNIX_CONNECT_ARGUMENT, UNIX_CONNECT (v0.115.0)
help Show Mix builtin help in the REPL
project_info Read-only workspace introspection: project_info() -> {root, main: {members: [crate names], exclude, toolchain}, desktop: bool} — the main workspace members (crates/* minus excludes), the pinned rust-toolchain channel, and whether the desktop workspace is present. For orchestration scripts, no ad-hoc TOML parsing (v0.103.15)
require Load a Mix module: evaluate the file once in an isolated scope, return its exports (map of top-level fns + $vars, or the file's top-level return value). Cached per canonical path; cycles error (v0.27.0)
format — see strings
fmt printf-style format string → string. Specs: %s %d %f %.Nf %Nd %-Ns %0Nd %% (v0.2.0; %0Nd zero-pad v0.54.0 — numeric only, use lpad(s,n,"0") for strings). Dynamic width * takes the width from the next argument: %*s %-*s %d %0d %*f (v0.63.0; the width argument must be a non-negative integer). For byte-exact C-printf parity (%e %g %x, flags, glibc float rounding) use sprintf()
sprintf C-compatible printf: sprintf(fmt, ...) → string. Conversions %d %i %u %o %x %X %f %F %e %E %g %G %s %c %%; flags - + 0 # and space; width/precision incl. *; length modifiers parsed and ignored (integer conversions are 64-bit, so a negative %u/%x prints its 64-bit two's-complement). Float output matches glibc printf byte-for-byte (fixture-tested) — for emitting numbers a C/Rust consumer compares exactly. Divergences: %s pads by codepoints and %.Ns truncates to ≤N bytes without splitting a codepoint; %c takes a Unicode scalar (v0.71.0)
printf Formatted write to stdout (no trailing newline — include \n explicitly) (v0.2.0)
eprintf Formatted write to stderr (v0.2.0)
format_bytes Format byte count as human-readable size (e.g. "1.5 MB"); a non-numeric argument raises (strict since v0.55.0)
format_number Format number with thousands separators; non-numeric value/decimals arguments raise (strict since v0.55.0)
json — see data
json_parse Parse JSON string into Mix value
json_encode Encode Mix value as JSON string
jq Run a jq filter; filter MUST yield 0 (→nil) or 1 (→value) output, >1 raises. jq(value, filter)
jq_all Run a jq filter, collect ALL outputs as a list (the stream case). jq_all(value, filter)
read_json Read a single-record JSON file directly into a Mix value (v0.2.3)
read_jsonl Read a JSON-lines file — list of records, strict by default, {skip_errors: true} for lenient (v0.2.3)
yaml_parse Parse a YAML string into a Mix value (requires yaml feature; v0.71.0). Single document by default (0 docs → nil, >1 raises); pass {docs:true} for a list of every document. Anchors/aliases resolved; scalar mapping keys become string keys; a non-scalar key raises
yaml_encode Encode a Mix value as YAML (requires yaml feature; v0.71.0). No leading --- marker, trailing newline guaranteed; whole numbers emit as integers; nil/bool/number/string/list/map only — bytes, buffer or function values raise
toml_parse Parse TOML string into Mix map
toml_encode Encode Mix value as TOML. Raises TOML_UNREPRESENTABLE with {path,type} details for nil, function, bytes, or buffer values instead of silently replacing them with empty strings (strict since v0.55.0)
data_parse Parse a strict-data .conf.mix string into a Mix value (inverse of data_encode) (v0.3.2)
data_encode Encode a Mix value as a strict-data .conf.mix string with correct $ / ~ / \ escaping; round-trips through data_parse. data_encode(value, [pretty]) — truthy 2nd arg emits multi-line indented output (v0.3.2)
validate — see data
require_key Assert a map key is present with a non-nil value and return it; raises VALIDATION_REQUIRED with details {path, expected, actual_type} (v0.29.0) expect_type Assert a value's type and return it: expect_type($v, "integer") — types: any nil bool number integer string bytes buffer list map function (integer = finite whole within ±2^53-1); raises VALIDATION_TYPE (v0.29.0) nonblank Assert a string contains a non-whitespace character, return it UNTRIMMED; the optional label names the value in the error; raises VALIDATION_NONBLANK — the boundary guard against nil/"" flowing into hostnames and paths (v0.29.0) get_or Map lookup with a default that covers BOTH an absent key and a nil value (the tolerant twin of require_key) (v0.29.0) validate Validate a map against a field spec at a job/API boundary: validate($raw, {node: {type: "string", nonblank: true}, plan: {enum: ["gold", "silver"]}, vmid: {type: "integer", min: 100, max: 999999}, tags: {required: false, type: "list", items: {type: "string"}}, owner: {type: "map", schema: {name: {nonblank: true}}}}). Rules: required (default TRUE) / type (string or list of types) / nonblank / enum / min / max / min_length / max_length / items / schema. Returns the ORIGINAL map unchanged; optional absent-or-nil fields skip their rules; unknown INPUT fields pass through; unknown RULE keys raise VALIDATION_SPEC; violations raise VALIDATION_* with details {path, expected, actual_type} — paths like owner.name and tags[2] (v0.29.0)
hof — see hof
sort_by Return list sorted ascending by key function (stable) (v0.2.0) filter Return new list of items where predicate returns truthy (v0.2.0) map Return new list of transform(item) results (v0.2.0) reduce Fold list left with an explicit init: reduce($xs, $init, function($a, $b) = ...) (v0.2.0) any Short-circuit: true if any item matches predicate (v0.2.0) all Short-circuit: true if every item matches predicate (v0.2.0) count Count items where predicate returns truthy (v0.2.0) min_by Return the ITEM (not the key) with minimum key-function value (v0.2.0) max_by Return the ITEM with maximum key-function value (v0.2.0) sum_by Sum of key(item) across all items, returns number (v0.2.0) group_by Map of stringified-key → list of items (first-seen key order) (v0.2.0) unique_by Dedup list by key function, first occurrence wins (v0.2.0)
bus — see bus
bus_call Call a host-injected Bus verb under delegated identity: bus_call(verb, args) → reply. The embedder bounds which verbs are reachable and injects the delegation envelope; the script names no host/peer/actor
publish One-call topic publish (0.63.0): publish(topic, body[, opts]) builds the SPEC-02 wire frame and sends it via noded topic.publish — no hand-built ---\n frames, no body=/name= header-route trap. body is the payload STRING (json_encode a map first); opts: {retain: bool, command: string (inner frame header override, defaults to topic), headers: map}. Sets $rc/$result like send; returns rc (0 = published)
serve_name The Bus service name this mix --serve citizen registered under — the --name value, else the script-stem derivation — or nil in a plain script or the REPL. Read it instead of hard-coding the name: a second instance started with --name other must publish other, not the first instance's name, in anything that routes replies or clicks back to it. $me = serve_name() ?? "quoin-panel" keeps a script runnable outside serve mode (v0.91.0)
is_reload_candidate True only while a --serve hot-reload candidate's init body is executing (false during the initial boot, in a plain script, and in every committed generation). A loader branches on it: the candidate must prepare passive state only — no starts, no spawns, no stopping old behaviour, no persisted writes — because a failed candidate reverts to the old evaluator with its managed children intact, and the committed generation reaps the old children before its own lifecycle.commit handler starts anything. Commit-time work belongs in an on lifecycle.commit handler, which the runtime queues locally exactly once per committed swap
send Send a request to a Bus port: send PORT VERB [ARGS...]. Reply bands in $rc: 0 delivered+accepted; 1..9 delivered with a warning (still success); >=10 peer application error (kept in $rc, details in $reply); -1 transport failure; -2 per-send timeout; -3 Bus unavailable. $result holds the reply body
emit Fire-and-forget Bus message (no reply is waited for): emit PORT VERB [ARGS...]
address Set the default Bus target for a block: address PORT ... end (bare send lines inside go to PORT)
on Register a Bus handler: on VERB ... end. A serve citizen answers VERB calls; reply() sends the answer. Topic subscribers match on the inner command
reply Answer the in-flight request the serve pump is dispatching (only inside an on handler); payload becomes the caller's $result
subscribe Register interest in a Bus topic so deliveries reach a matching on handler: subscribe("topic.name")
unsubscribe Drop interest in a Bus topic: unsubscribe("topic.name")
port_exists True when a Bus port exists on the broker
bus_reconnect Reconnect the Bus broker session
noded_register Register this node with noded (identity/bootstrap)
db — see capabilities
db_query Query the host-injected scoped DB: db_query(sql, [params]) → rows db_exec Exec on the host-injected scoped DB: db_exec(sql, [params]) → {affected, last_insert_id}
jmap — see capabilities
jmap Call the host-injected JMAP upstream: jmap(method, args) → result, or jmap([[method,args,callId],…]) → methodResponses jmap_upload Upload bytes as a JMAP blob via the host-injected upstream: jmap_upload(body[, content_type]) → blobId (the compose half of the mail seam; Email/set create is blob-only)
datastar — see datastar
ds_patch_elements Frame an HTML fragment as a Datastar patch-elements SSE event: ds_patch_elements(html, [{selector, mode, view_transition}]) → event string. mode=outer(default)/inner/remove/replace/prepend/append/before/after. Caller MUST html_escape() untrusted content first — this only frames (requires datastar feature) (v0.18.1) ds_patch_signals Frame a signal update as a Datastar patch-signals SSE event: ds_patch_signals(signals_map_or_json, [{only_if_missing}]) → event string. A map is JSON-encoded; a string is used verbatim (requires datastar feature) (v0.18.1) ds_sse Assemble a text/event-stream response body from one event string or a list of them: ds_sse(event | [events]) → body. Pair with headers={"Content-Type":"text/event-stream"} (requires datastar feature) (v0.18.1)
Prelude — Mix-defined helpers, not builtins
mix help also lists a small prelude (sum, avg): plain Mix functions auto-loaded from std/prelude.mix before every script and ~/.mixrc. They are not builtins — mix builtins and mix what do not know them. A builtin always shadows a same-named Mix function, which is why the old prelude min/max/abs/clamp shims were retired when the native math category landed in v0.19.0, and why the lines/chars/read_lines shims went with the 0.63.0 native lines/chars (the read_lines builtin had shadowed its shim since v0.2.3).
See also
- overview · keywords · the mix CLI
mix builtins [CATEGORY]lists one category with one-line descriptions (mix builtinsalone lists them all);mix what NAMEdescribes one builtin or keyword;mix helpprints the compact category / keyword / subcommand summary.