← Back to site Loading…

rescriptum

Command line

Guide

Generated on August 30, 2026

Command line

Command line

With no arguments, rescriptum runs the server. Everything else is a subcommand.

CommandDoes
rescriptumrun 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 checkrender 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 --applymove those documents into a directory each
rescriptum configshow the configuration, and where each value comes from
rescriptum config --jsonthe same, for a settings panel
rescriptum config --value KEYone 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 --helpusage 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
FormFacts 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 FILEthe 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. So render … > answer.toml gives 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

StatusMeans
0success
1the 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.