rescriptum
Command line
Guide
Command line
Command line
With no arguments, rescriptum runs the server. Everything else is a subcommand.
| Command | Does |
|---|---|
rescriptum | run the server |
rescriptum render <id> | print the answer that identifier would receive |
rescriptum render --body FILE | …for a captured request body |
rescriptum render --query Q | …for labels, e.g. "mac=aa:bb&serial=7ABC1" |
rescriptum check | render everything in the configured store and report what breaks |
rescriptum import <dir> | copy a directory of documents into the configured store |
rescriptum export <dir> | write the configured store out as a directory of documents |
rescriptum migrate [<dir>] | show what a flat answers directory would become |
rescriptum migrate --apply | move those documents into a directory each |
rescriptum config | show the configuration, and where each value comes from |
rescriptum config --json | the same, for a settings panel |
rescriptum config --value KEY | one value, for a script — never a credential |
rescriptum config set K=V … | edit the file RESCRIPTUM_CONFIG or RESCRIPTUM_ENV_FILE names |
rescriptum config unset KEY … | take a setting back out of it |
rescriptum --help | usage and the environment variables |
All of them read the same environment variables, including
RESCRIPTUM_CONFIG and
RESCRIPTUM_ENV_FILE — which are resolved first, so a
file that cannot be read stops any command that needs configuration. --help and
--version are answered before it is read, because they are what you reach for when
something is wrong. There are no global flags.
render
$ rescriptum render 98:fa:9b:50:d8:10
$ rescriptum render --query "serial=7ABC123&mac=98:fa:9b:50:d8:10"
$ rescriptum render --query "path=/rhel/ks&serial=7ABC123"
$ rescriptum render --body /var/log/rescriptum-captures/2026…-0000.body
| Form | Facts it supplies |
|---|---|
<id> | the identifier as a haystack, and nothing else — enough to match by name, not enough for a selector on serial |
--query "k=v&k2=v2" | those labels, percent-decoded. path= also yields file and segment, and constrains the format the way a real URL would |
--body FILE | the file verbatim: haystack, plus flattened JSON if it parses as JSON |
- The document goes to stdout; the
# format=… machine=… group=…line explaining how it was reached goes to stderr. Sorender … > answer.tomlgives you just the document. - Load-time problems are printed as
warning:lines first. - Exit 0 when something resolved, 1 when nothing applied (the server would have
returned
404) or rendering failed.
check
$ rescriptum check
Reports load-time problems, renders every machine document and every group member, names
the groups that select on a match block (which it cannot try without a real request),
and calls the installer’s own validator where one is on PATH.
Exit 0 when everything renders, 1 when anything failed — so it drops into CI as is. See validating.
import / export
$ RESCRIPTUM_STORE=sqlite RESCRIPTUM_DB_PATH=/srv/answers.db rescriptum import /srv/answers
$ RESCRIPTUM_STORE=sqlite RESCRIPTUM_DB_PATH=/srv/answers.db rescriptum export /tmp/backup
import reads a directory and writes into the configured store; export does the
reverse. The round trip is byte-identical, paths included. Neither runs check for you —
the output says to.
migrate
Answers used to be files at the top of the answers directory — 98fa9b50d810.toml beside
98fa9b50d810.preseed. They are now a directory each, and a document left flat is
reported and not served. This moves them:
$ rescriptum migrate
migrating /srv/answers
98fa9b50d810.toml -> 98fa9b50d810/proxmox.toml
98fa9b50d810.ipxe -> 98fa9b50d810/boot.ipxe
groups/rack-a.toml -> groups/rack-a/proxmox.toml
default.toml -> default/proxmox.toml
4 document(s) to move — nothing has been changed. Re-run with --apply.
It shows by default and moves only when told to. The answers directory is what a rack installs from; typing the command to find out what it would do must not rearrange it.
--apply performs the moves, each a rename within the same directory, so no document is
ever rewritten. If any destination is already taken, nothing moves at all — including
the documents that could have — and the collisions are named: a half-migrated directory is
the state nobody can reason about. It takes a directory as an argument, defaulting to
RESCRIPTUM_ANSWERS_DIR, and running it on an already-migrated directory says there is
nothing to move.
config
The configuration is environment variables, and on a deployment that reads them from a file — a packaged install, mainly — this is how to see and change them without opening an editor. It is also what the DSM application runs underneath.
$ rescriptum config
env file: /var/packages/rescriptum/etc/rescriptum.env
RESCRIPTUM_STORE files default
RESCRIPTUM_ANSWERS_DIR /volume1/netboot/answers file
RESCRIPTUM_LISTEN_ADDR 0.0.0.0:9000 environment
RESCRIPTUM_ADMIN_TOKEN (set) file
The third column is the point. The files supply defaults and the real environment
wins, so a value marked environment cannot be changed by editing a file — and config set says so rather than letting you write something the running server will ignore. With
a TOML file the column reads toml file, and naming both files prints both paths plus the
order they win in.
config set writes the TOML file when both are named, because it is the one the server
reads first: writing the other would be a change that silently does nothing.
A credential is never printed, by any form of this command. A token shows as (set) or
(not set); --value refuses outright.
$ rescriptum config set RESCRIPTUM_LOG=problems RESCRIPTUM_CAPTURE_DIR=/srv/captures
wrote /var/packages/rescriptum/etc/rescriptum.env
Writing keeps the file as it is otherwise: comments stay, a setting is replaced where it
stands, and one that is commented out is uncommented in place rather than appended
below — which matters when the comment above it is the only documentation the file has. In
a TOML file the same care applies to the document: the value is replaced where it stands,
its trailing comment survives, and config unset empties the value rather than deleting
the line, so the paragraph explaining the setting stays where it was.
Two refusals are deliberate:
- A change that would leave a server unable to start is refused, whole, before anything is written. Turning on the admin API without a token, or with a token shorter than 16 characters, gets the reason instead of a broken next boot.
- A misspelled variable is refused. Written, it would be read back as a stranger and warned about at the next start, by which time nobody connects the two.
Unlike every other subcommand, this one works when the configuration is too broken to start a server — a file that will not parse, a token one character short. That is the state people run it to get out of.
media
Boot media: the installer images this server holds. Every one of these needs
RESCRIPTUM_MEDIA_DIR; without it they say so and exit 1. See
Serving boot media.
$ rescriptum media list # what is held: family, architecture, version, digest
$ rescriptum media add FILE [--sha256 D] # register one already in the directory
$ rescriptum media add URL --sha256 D # fetch one into it, then register it
$ rescriptum media check # re-verify every recorded digest
$ rescriptum media ipxe ID # print the .ipxe answer that boots one image
$ rescriptum media prepare ID [--url URL] # a Proxmox image with its answer URL inside
$ rescriptum media export ID FILE # materialise a prepared entry, for a stick
media add takes a file already inside the media directory — nothing is downloaded
and nothing is copied. It hashes it with progress, probes it, and writes a .media
sidecar beside it. --sha256 is checked before anything is recorded: a mismatch writes
nothing and exits 1.
media check’s exit status is a contract, like check’s. deploy.sh keys on it.
media ipxe prints to stdout and puts everything else on stderr, so
rescriptum media ipxe pve-8.4 > groups/rack-a/boot.ipxe produces a usable answer document —
which is all it is. It prints a script; it does not install one.
boot
The netboot half: TFTP’s loaders, the generated DHCP configuration, and the two scripts a machine executes. See Netbooting a machine.
$ rescriptum boot dhcp-snippet [--format F] [--one-loader]
$ rescriptum boot check # are the loaders a snippet names actually on disk?
$ rescriptum boot bootstrap # print the stage-two script
$ rescriptum boot menu # print the built-in menu
--format is dnsmasq (the default), isc, kea, powershell, pfsense or
mikrotik. The snippet goes to stdout and warnings to stderr, so
boot dhcp-snippet > dhcpd.conf produces a file that can be included as-is.
boot check’s exit status is a contract, like check’s. What it catches is the least
diagnosable failure in the chain: a snippet naming a loader that is not on disk fails
silently at the ROM, with nothing on any console. It also warns when the media listener
has moved off the port shipped loaders embed.
boot bootstrap and boot menu print what a machine will execute, for the same reason
render prints an answer: everything a rack runs should be readable by a human first.
Exit statuses
| Status | Means |
|---|---|
0 | success |
1 | the command failed — nothing resolved, a document would not parse, the store could not be opened |
config is the one with a second meaning: 0 says the configuration is one the server
would start on, 1 that it is not — or that a write was refused. That makes it usable
from a script the way check is.
The server itself exits 0 on SIGTERM or Ctrl-C, and 1 if it cannot bind or cannot
open the store.