rescriptum
Référence
Guide
Référence
Référence
La contrepartie exhaustive du reste du guide. Tables et contrats, une page par surface :
- Configuration — chaque variable d’environnement, sa valeur par défaut, et ce que fait une valeur invalide.
- Surface HTTP — méthodes, codes de statut, en-têtes, limites.
- Formats et alias d’endpoint — quelle extension est quel format, et quel segment d’URL le demande.
- Ligne de commande — chaque sous-commande et chaque option.
Vous cherchez le récit plutôt ? Commencez par ce qu’est rescriptum.
Configuration
Configuration
Des variables d’environnement — et, en option, un fichier d’où les lire, dans l’une de deux formes. Il n’y a pas de ligne de commande à se tromper, et les variables sont toute la configuration : les deux formats de fichier règlent exactement les mêmes choses sous exactement les mêmes règles, donc rien de ce qu’on écrit dans un fichier ne peut signifier quelque chose que l’environnement ne dirait pas.
Les variables
| Variable | Défaut | Signification |
|---|---|---|
RESCRIPTUM_CONFIG | non défini | Lire les valeurs par défaut depuis ce fichier TOML — voir plus bas |
RESCRIPTUM_ENV_FILE | non défini | Lire les valeurs par défaut depuis ce fichier CLÉ=valeur — voir plus bas |
RESCRIPTUM_STORE | files | files (un répertoire) ou sqlite (une base) |
RESCRIPTUM_ANSWERS_DIR | /srv/answers | Répertoire des documents de réponse |
RESCRIPTUM_DB_PATH | /srv/answers.db | Chemin de la base, quand RESCRIPTUM_STORE=sqlite |
RESCRIPTUM_LISTEN_ADDR | 0.0.0.0:8000 | Adresse d’écoute. :0 choisit un port libre, et celui qui est bindé est affiché |
RESCRIPTUM_WORKERS | nombre de CPU | Threads du runtime asynchrone. Pas une limite de concurrence |
RESCRIPTUM_MAX_CONNECTIONS | 2048 | Connexions en vol avant délestage en 503 |
RESCRIPTUM_TIMEOUT_SECS | 10 | Délai de lecture des en-têtes et échéance de la connexion entière |
RESCRIPTUM_ANSWER_TOKEN | non défini | Jeton exigé par l’endpoint de réponse. Non défini = ouvert |
RESCRIPTUM_ADMIN_ADDR | non défini | Listener de l’API d’administration. Non défini = API désactivée |
RESCRIPTUM_ADMIN_TOKEN | non défini | Jeton d’administration, 16+ caractères. Obligatoire avec RESCRIPTUM_ADMIN_ADDR |
RESCRIPTUM_CAPTURE_DIR | non défini | Enregistre les corps de requête ici. Non défini = pas de capture |
RESCRIPTUM_LOG | all | all, problems ou off — voir plus bas |
RESCRIPTUM_LOG_FILE | non défini | Un fichier où ajouter, ou stdout / stderr. Non défini = stderr |
RESCRIPTUM_MEDIA_DIR | non défini | Images d’installation. Non défini = pas de média et pas de listener média |
RESCRIPTUM_MEDIA_ADDR | 0.0.0.0:8001 | Le listener média, quand un répertoire de médias existe |
RESCRIPTUM_MEDIA_TIMEOUT_SECS | 600 | Échéance du transfert entier. Volontairement pas les 10 s du point de réponse |
RESCRIPTUM_MEDIA_MAX_CONNECTIONS | 16 | Transferts simultanés. Bas exprès : chacun retient son jeton des minutes durant |
RESCRIPTUM_PUBLIC_HOST | déduit | L’hôte que nomment les URL générées. Un hôte, jamais une URL |
RESCRIPTUM_BOOT_ALLOW | non défini | CIDR clients autorisés à récupérer les médias. Non défini = quiconque atteint le port |
RESCRIPTUM_BOOT_DIR | non défini | Chargeurs et menus, distribués en TFTP. Non défini = pas de TFTP du tout |
RESCRIPTUM_TFTP_ADDR | 0.0.0.0:69 | Le listener TFTP, ou off pour aucun. Le port 69 est privilégié ; voir RESCRIPTUM_USER |
RESCRIPTUM_TFTP_PORT_RANGE | non défini | Les ports depuis lesquels un transfert répond, en premier-dernier. Un transfert TFTP quitte le port 69 aussitôt — le serveur répond depuis un port neuf et le client acquitte vers celui-là — donc un pare-feu n’autorisant que 69 jette l’acquittement, et la machine semble s’être désintéressée. Épinglez la plage pour pouvoir l’ouvrir. Non définie, le noyau choisit |
RESCRIPTUM_TFTP_BLKSIZE | 1468 | Le plus grand bloc TFTP accepté. 1468 remplit exactement un chemin de 1500 octets — 1468 de charge, 4 TFTP, 8 UDP, 20 IP — donc un tag VLAN ou un tunnel rend la trame trop grande et une ROM PXE s’arrête en général sans rien dire. À baisser (1400, ou 512) quand un démarrage cale au premier bloc |
RESCRIPTUM_BOOT_TIMEOUT_SECS | 15 | Secondes avant que le menu ne retombe sur le disque local |
RESCRIPTUM_BOOT_UNCLAIMED | menu | Ce que reçoit une machine qu’aucune réponse ne revendique. local la rend à son firmware, ce qui inverse le sens d’un fichier de réponse : présent veut dire installe celle-ci plutôt que laisse celle-ci tranquille |
RESCRIPTUM_INSTALLED_TOKEN | non défini | Le jeton du [post-installation-webhook] de Proxmox. Défini, POST /installed existe et retire la revendication d’installation d’une machine quand elle signale sa réussite. Non défini, il n’y a pas d’endpoint |
RESCRIPTUM_BOOT_LOGO | intégré | Un PNG à afficher derrière le menu |
RESCRIPTUM_BOOT_TITLE | intégré | La barre de titre du menu |
RESCRIPTUM_USER / _GROUP | non défini | Basculer dessus après avoir lié. L’ordre inverse échoue au déploiement |
/srv est l’endroit où la norme de hiérarchie des fichiers range les données servies par le
système, ce qu’est précisément un répertoire de réponses. Les deux valeurs par défaut y vivent,
pour qu’un rescriptum lancé sans rien fasse quelque chose de plausible sur n’importe quel
hôte Linux. Rien ne crée le répertoire pour vous, et le serveur le signale au démarrage s’il
manque.
Journalisation
Une ligne par événement, sur stderr par défaut. Deux réglages, parce que les deux questions sont différentes.
Quoi — RESCRIPTUM_LOG :
| Valeur | Garde |
|---|---|
all (défaut) | chaque requête, plus le démarrage, les avertissements et les erreurs |
problems | démarrage, avertissements, erreurs, et seulement les requêtes qui n’ont pas abouti |
off / none | rien du tout |
Une réponse réussie fait une ligne, et à treize mille requêtes par seconde c’est la seule
chose ici qui ait du volume. problems est ce que vous voulez quand un déploiement devient
routinier et que le disque, lui, ne l’est pas. Tout le reste est peu volumineux et
diagnostique, donc conservé dans les deux cas.
Une requête qui n’a jamais atteint de statut — une connexion expirée en plein corps —
compte comme un problème. Une valeur non reconnue retombe sur all avec un avertissement :
une faute de frappe ne doit pas être la raison pour laquelle personne ne voit pourquoi un
déploiement a échoué. Le niveau est nommé dans la ligne de démarrage (log=problems), donc
un log vide s’explique lui-même.
Où — RESCRIPTUM_LOG_FILE :
| Valeur | Va vers |
|---|---|
non définie, ou stderr | stderr, ce que lit un superviseur |
stdout | stdout |
| toute autre valeur | ce fichier, en ajout ; les répertoires parents sont créés |
Un fichier impossible à ouvrir est une erreur de démarrage, pas un repli sur stderr — ce serait une surprise silencieuse découverte bien plus tard. Une écriture qui échoue une fois le serveur lancé est abandonnée : un serveur de provisioning qui mourrait parce que son disque de logs est plein ferait échouer toutes les installations en cours pour signaler qu’il ne peut pas signaler quelque chose.
La rotation vous incombe. Sous systemd il n’y a rien à faire, le log part dans le journal ;
avec un fichier, pointez logrotate dessus avec copytruncate.
Le fichier TOML
RESCRIPTUM_CONFIG nomme un fichier en TOML qui règle les mêmes variables, dans une forme
faite pour être lue. C’est celui vers lequel se tourner quand une personne édite le
fichier à la main — sur un NAS, dans File Station ou via SMB — c’est-à-dire exactement là
où RESCRIPTUM_ANSWERS_DIR=… sur chaque ligne se lit mal, et où le mot « environnement »
envoie chercher un shell qui n’existe pas.
# /etc/rescriptum.toml (chmod 600, appartenant à root)
answers_dir = "/srv/answers"
listen_addr = "0.0.0.0:8000"
log = "problems" # all | problems | off
[store]
kind = "sqlite"
db_path = "/srv/answers.db"
[server]
workers = 2
max_connections = 2048
timeout_secs = 10
[admin]
addr = "127.0.0.1:8001"
token = "…"
[answer]
token = "…"
capture_dir = "/var/lib/rescriptum/captures"
$ RESCRIPTUM_CONFIG=/etc/rescriptum.toml rescriptum
2026-08-29T12:42:02Z - reading configuration defaults from /etc/rescriptum.toml (8 set)
Toutes les règles du fichier d’environnement valent ici aussi : jamais découvert,
seulement nommé (il n’y a pas de ./rescriptum.toml) ; l’environnement réel gagne ;
et un fichier demandé et illisible est une erreur de démarrage, jamais un
avertissement.
Placez-le hors du répertoire de réponses. Tout .toml servable à la racine de ce
répertoire est un document réponse, et ce format partage l’extension — un fichier de
configuration déposé là est signalé par check comme une réponse mal placée, et migrate
propose de le déplacer. /etc est le foyer évident ; sur une installation empaquetée, le
paquet en choisit un.
Les noms
Le préfixe disparaît et les tables font le regroupement. Rien d’autre ne change : chaque
ligne ci-dessous est la variable du même nom, et rescriptum config affiche les deux
orthographes.
| Dans le fichier | Variable |
|---|---|
answers_dir | RESCRIPTUM_ANSWERS_DIR |
listen_addr | RESCRIPTUM_LISTEN_ADDR |
log, log_file | RESCRIPTUM_LOG, RESCRIPTUM_LOG_FILE |
public_host | RESCRIPTUM_PUBLIC_HOST |
user, group | RESCRIPTUM_USER, RESCRIPTUM_GROUP |
store.kind, store.db_path | RESCRIPTUM_STORE, RESCRIPTUM_DB_PATH |
server.workers, server.max_connections, server.timeout_secs | RESCRIPTUM_WORKERS, RESCRIPTUM_MAX_CONNECTIONS, RESCRIPTUM_TIMEOUT_SECS |
admin.addr, admin.token | RESCRIPTUM_ADMIN_ADDR, RESCRIPTUM_ADMIN_TOKEN |
answer.token, answer.capture_dir | RESCRIPTUM_ANSWER_TOKEN, RESCRIPTUM_CAPTURE_DIR |
media.dir, media.addr, media.timeout_secs, media.max_connections | les quatre RESCRIPTUM_MEDIA_* |
boot.dir, boot.allow, boot.unclaimed, boot.timeout_secs, boot.logo, boot.title | les six RESCRIPTUM_BOOT_* |
tftp.addr, tftp.port_range, tftp.blksize | les trois RESCRIPTUM_TFTP_* |
installed.token | RESCRIPTUM_INSTALLED_TOKEN |
Le format
| N’importe quel scalaire TOML | un nombre peut s’écrire en nombre (workers = 2) ou en chaîne ; les deux arrivent au serveur comme le même réglage |
Un commentaire # | n’importe où, y compris en fin de ligne — contrairement au fichier d’environnement, qui n’a pas d’échappements et ne peut donc pas en avoir |
Une valeur avec un #, un guillemet ou une espace | sans problème, échappée comme TOML échappe, et relue à l’identique |
"" | non défini — la même règle qu’une variable exportée mais vide, et c’est ce qui permet à config unset de vider une ligne au lieu de supprimer le paragraphe qui la documente |
| La même clé deux fois | refusée par TOML lui-même, donc le fichier ne se charge pas |
| Une clé que ce programme ne lit pas | un avertissement la nommant : admin.tokenn est attrapé au lieu d’être ignoré |
| Une liste ou une table là où une valeur est attendue | une erreur de démarrage : contrairement à une faute de frappe, elle visait un réglage réel, et servir la valeur par défaut alors que le fichier dit le contraire serait silencieux |
| Un fichier lisible par d’autres | un avertissement avec son mode, parce qu’il peut contenir admin.token |
Les avertissements nomment les clés et les chemins, jamais les valeurs.
Les deux fichiers à la fois
Nommer les deux est une transition plutôt qu’un état stable : rien n’est refusé et l’ordre
est annoncé au démarrage — l’environnement bat le fichier TOML, qui bat le fichier
d’environnement. rescriptum config montre lequel des trois a mis chaque valeur en
vigueur, et config set écrit dans le fichier TOML — celui que le serveur lit en premier,
pour qu’une écriture ne puisse pas être une modification qui ne change rien en silence.
Le fichier d’environnement
RESCRIPTUM_ENV_FILE nomme un fichier contenant les mêmes variables. Il existe pour les
déploiements qui n’ont nulle part où mettre un jeton — au premier chef Synology DSM 7,
qui n’a pas de systemd. Sous systemd, EnvironmentFile= fait déjà cela et vous n’en avez
pas besoin.
# /etc/rescriptum.env (chmod 600, appartenant à root)
RESCRIPTUM_STORE=sqlite
RESCRIPTUM_DB_PATH=/srv/answers.db
RESCRIPTUM_ADMIN_ADDR=127.0.0.1:8001
RESCRIPTUM_ADMIN_TOKEN=…
$ RESCRIPTUM_ENV_FILE=/etc/rescriptum.env rescriptum
2026-08-24T12:42:02Z - reading configuration defaults from /etc/rescriptum.env (4 set)
Il n’est jamais découvert, seulement nommé. Il n’y a pas de ./.env. Ce binaire tourne
en root : s’il ramassait un fichier dans le répertoire d’où il a été lancé, quiconque peut
y écrire posséderait RESCRIPTUM_ADMIN_TOKEN — et avec lui le mot de passe root de chaque
machine installée ensuite.
L’environnement réel gagne. Le fichier fournit des valeurs par défaut, donc ce qui est exporté délibérément au lancement n’est jamais écrasé en douce. Une variable exportée mais vide compte comme non définie, donc le fichier s’applique quand même.
Un fichier demandé et illisible est une erreur de démarrage, pas un avertissement. C’est tout l’intérêt : l’échec qu’il remplace est un serveur qui démarre sur ses valeurs par défaut — mauvais répertoire de réponses, pas de jeton admin — sans un mot dans le log.
Le format
CLÉ=valeur, une par ligne | un export en tête est accepté, pour que le même fichier puisse aussi être sourceé |
# en début de ligne | un commentaire |
# ailleurs | fait partie de la valeur. Pas de commentaires en fin de ligne : tronquer un jeton sur un # qu’il contient légitimement serait silencieux, alors qu’un commentaire atterrissant dans une valeur est bruyant |
"guillemets" ou 'guillemets' | les guillemets sont retirés et les espaces internes conservés ; une valeur sans guillemets est trimée |
$HOME, ${x} | non développés. Ce n’est pas un shell — pas de substitution, pas de lignes de continuation |
| la même clé deux fois | une erreur de démarrage, plutôt qu’une supposition sur celle qui était voulue |
| une clé que ce programme ne lit pas | un avertissement nommant la clé — RESCRIPTUM_ADMIN_TOKENN est donc attrapé au lieu d’être ignoré |
| un fichier lisible par d’autres | un avertissement avec son mode, parce qu’il peut contenir le jeton admin |
Les avertissements nomment les clés et les chemins, jamais les valeurs.
Le lire et le modifier
rescriptum config affiche chaque variable, sa valeur, et qui des fichiers ou de
l’environnement l’y a mise — la distinction qui compte, puisque les fichiers fournissent
des valeurs par défaut et que l’environnement réel l’emporte. config set modifie le
fichier comme on voudrait qu’il le soit, dans l’un ou l’autre format : commentaires
conservés, réglage commenté décommenté sur place plutôt que dupliqué (fichier
d’environnement) ou valeur remplacée là où elle est (TOML), et refus avant toute écriture
d’une modification qui laisserait
un serveur incapable de démarrer. C’est documenté dans la
référence de la ligne de commande, et c’est ce que
l’application DSM pilote dessous.
Valeurs invalides
| Cas | Ce qui se passe |
|---|---|
Exportée mais vide (RESCRIPTUM_LISTEN_ADDR=) | traitée comme non définie — une valeur vide est une erreur, pas une instruction |
| Uniquement des espaces | pareil, et les valeurs sont trimées |
| Un nombre nul ou impossible à parser | retombe sur la valeur par défaut, plutôt que de démarrer un serveur qui accepte des connexions sans jamais répondre |
RESCRIPTUM_STORE avec toute autre valeur | un avertissement, et files est utilisé |
RESCRIPTUM_ENV_FILE ou RESCRIPTUM_CONFIG nommant un fichier absent, illisible ou malformé | une erreur de démarrage |
| Un réglage TOML recevant une liste ou une table | une erreur de démarrage, contrairement à une clé mal orthographiée, qui avertit |
RESCRIPTUM_STORE=sqlite sur un binaire construit sans la feature | une erreur au démarrage |
Erreurs de démarrage
Celles-ci arrêtent le serveur au lieu d’avertir, parce que démarrer quand même serait pire :
| Condition | Pourquoi c’est fatal |
|---|---|
RESCRIPTUM_ADMIN_ADDR défini avec RESCRIPTUM_STORE autre que sqlite | deux façons de changer la même configuration, en concurrence |
RESCRIPTUM_ADMIN_ADDR défini sans RESCRIPTUM_ADMIN_TOKEN | une API ouverte qui réécrit les identifiants root |
RESCRIPTUM_ADMIN_TOKEN de moins de 16 caractères | assez court pour être deviné |
| L’adresse d’écoute ne peut pas être bindée | rien à faire |
| Le store ne peut pas être ouvert | rien à servir |
RESCRIPTUM_MEDIA_ADDR défini sans RESCRIPTUM_MEDIA_DIR | un listener sans rien à servir |
RESCRIPTUM_MEDIA_ADDR égal à l’adresse de réponse ou d’administration | le second bind perd, et lequel dépend de l’ordre de démarrage |
RESCRIPTUM_PUBLIC_HOST portant un schéma, un port ou un chemin | il est écrit dans les URL de deux listeners ; un port dans la valeur épingle chaque script généré sur l’un d’eux |
RESCRIPTUM_TFTP_ADDR défini sans RESCRIPTUM_BOOT_DIR | un listener sans chargeur à distribuer |
| Le répertoire de démarrage ne peut pas être résolu | chaque contrôle de chemin s’y compare |
RESCRIPTUM_USER nomme un compte inexistant | rien à devenir |
Avertissements de démarrage
Ceux-ci sont affichés et le serveur continue :
| Condition | Ligne |
|---|---|
| Répertoire de réponses absent | warning: … does not exist yet — every request will 404 until it does |
| Le chemin existe mais n’est pas un répertoire | warning: … is not a directory — every request will 404 until it is |
| Répertoire de réponses présent mais illisible | warning: … cannot be read: … — every request will 404 until that is fixed. La cause la plus probable est un serveur tournant sous un utilisateur qui n’est pas le propriétaire du répertoire |
| API d’administration hors boucle locale | warning: the admin API is not bound to loopback — … |
RESCRIPTUM_ANSWER_TOKEN de moins de 16 caractères | un avertissement, pas une erreur — refuser de démarrer laisserait un parc incapable de s’installer |
| Tout problème dans le jeu de réponses | une ligne warning: chacun, le même jeu que signale check |
RESCRIPTUM_PUBLIC_HOST non défini | La réponse de la table de routage, ou l’unique adresse d’interface s’il n’y a pas de route par défaut. Journalisé dans les deux cas, en avertissement nommant les autres adresses s’il y en a. Un hôte derrière du NAT se trompe toujours en silence |
| TFTP ne peut pas se lier | warning: cannot bind TFTP on … — le seul listener dont l’échec de liaison n’est pas fatal. Le port 69 est le seul port privilégié de la conception, donc le seul bind qui puisse échouer pour quelque chose que personne n’a configuré ; les réponses sont le produit, et mourir ferait échouer toutes les installations en cours pour signaler qu’un second port n’a pas pu être ouvert. boot check sort en non-zéro et le message nomme les façons d’obtenir le port |
| Répertoire de médias absent ou illisible | une ligne warning: media: … — un parc ne doit jamais être incapable de s’installer parce qu’une image est bizarre |
Options de compilation
| Feature | Défaut | Effet |
|---|---|---|
sqlite | activée | Le store SQLite et l’API d’administration |
boot | activée | Le catalogue de médias, le lecteur ISO et le listener média |
Mesuré sur ARMv7 (gnueabihf, plancher glibc 2.17), les quatre d’affilée le 2026-08-29. Remesurez plutôt que de citer ces chiffres : ils ont bougé d’environ 375 Ko quand cette cible est passée de musl à glibc, et le jeu qu’ils remplacent ici avait dérivé d’environ 200 Ko.
| Build | Octets |
|---|---|
| les deux (défaut) | 2 813 712 |
sqlite seule | 2 557 592 |
boot seule | 1 649 048 |
| aucune | 1 392 544 |
Limites fixes
Non configurables, et délibérément :
| Limite | Valeur | Où |
|---|---|---|
| Corps de requête | 1 Mo | endpoint de réponse — un Content-Length invraisemblable est refusé depuis l’en-tête |
| Taille d’un document | 256 Ko | PUT de l’API d’administration |
| Requêtes capturées | 1000 captures | comptées depuis le répertoire au démarrage, donc un redémarrage ne repart pas de zéro |
| Échecs d’administration avant blocage | 5 en 60 s | le blocage double jusqu’à un maximum de 900 s |
| Adresses suivies par le garde-fou | 4096 | pour qu’il ne puisse pas être transformé en fuite mémoire |
| Filet de rechargement du listing | 1 s | force une relecture même quand le mtime du répertoire semble inchangé |
Surface HTTP
Surface HTTP
Deux listeners, et ils ne partagent jamais un port. L’endpoint de réponse est ce à quoi parlent les installateurs ; l’API d’administration est désactivée sauf configuration.
L’endpoint de réponse
| Requête | Réponse |
|---|---|
POST n’importe quel chemin | la réponse, typée par son format |
GET n’importe quel chemin | la même chose |
GET /health | 200 OK, corps OK\n — pas de jeton nécessaire, jamais limité en débit |
| toute autre méthode | 405 |
N’importe quel chemin, parce que l’URL est gravée dans une ISO et que ce serveur n’a pas
son mot à dire. Le chemin n’est pas ignoré pour autant : les segments nommant un
alias de format restreignent les documents pouvant répondre, et le chemin
fournit aussi les faits
path, file et segment.
Codes de statut
| Code | Quand |
|---|---|
200 | une réponse s’est appliquée |
400 | le corps n’a pas pu être lu |
401 | RESCRIPTUM_ANSWER_TOKEN est défini et la requête ne l’a pas présenté |
404 | rien n’a revendiqué la requête et il n’y a pas de default pour le format demandé |
405 | une méthode autre que GET ou POST |
413 | corps de plus de 1 Mo, ou Content-Length en annonçant un |
500 | un document ne parse pas, un groupe manque, un template n’a pas pu être rempli, ou la recherche a paniqué |
503 | à RESCRIPTUM_MAX_CONNECTIONS — écrit immédiatement, puis la connexion se ferme |
En-têtes de réponse
| En-tête | Valeur |
|---|---|
Content-Type | selon le format de la réponse — voir la table ci-dessous |
Content-Length | toujours défini |
Connection | close |
WWW-Authenticate | Bearer, sur un 401 |
| Format | Content-Type |
|---|---|
toml, et tous les formats texte (ks, preseed, cfg, seed, ipxe) | text/plain; charset=utf-8 |
yaml, yml | text/yaml; charset=utf-8 |
json, ign | application/json |
xml, autoyast, unattend | application/xml; charset=utf-8 |
Le TOML est servi en text/plain plutôt qu’en application/toml parce que c’est ce
qu’attend l’installateur Proxmox.
Limites de traitement des requêtes
| Plafond du corps | 1 Mo. Un Content-Length invraisemblable est refusé depuis l’en-tête, avant toute lecture — plutôt que d’allouer pour lui et de faire sauter une limite plus tard |
| Délai de lecture des en-têtes | RESCRIPTUM_TIMEOUT_SECS, 10 s par défaut |
| Échéance de la connexion entière | la même valeur. Les deux sont nécessaires : le délai d’en-têtes s’arrête à la fin des en-têtes, donc un client qui promet un corps sans l’envoyer garerait sinon une connexion indéfiniment |
| Concurrence | RESCRIPTUM_MAX_CONNECTIONS en vol ; au-delà, un 503 et fermeture plutôt qu’une mise en file |
| Authentification | seulement quand RESCRIPTUM_ANSWER_TOKEN est défini. Comparée en temps constant. Les échecs sont journalisés et jamais limités en débit |
Authentification
Authorization: Bearer <RESCRIPTUM_ANSWER_TOKEN>
Proxmox l’envoie quand son ISO a été préparée avec --answer-auth-token. Rien d’autre ne le
peut, ce qui est pourquoi c’est désactivé par défaut. Voir
Sécurité.
L’API d’administration
Un listener séparé, RESCRIPTUM_ADMIN_ADDR, en SQLite uniquement. Détails complets sur
sa propre page.
| Requête | Rôle |
|---|---|
GET /machines, GET /groups | lister les identifiants |
GET /machines/{id}, GET /groups/{name}, GET /default | le document stocké, tel qu’écrit |
PUT /machines/{id}, PUT /groups/{name}, PUT /default | stocker un document |
DELETE /machines/{id}, DELETE /groups/{name}, DELETE /default | en supprimer un |
GET /resolve/{id} | la réponse fusionnée que cette machine recevrait |
GET /check | les problèmes actuels |
GET /health | vivacité — pas de jeton, jamais bloqué |
Tous les endpoints de document prennent ?format=<ext>, toml par défaut.
| Code | Quand |
|---|---|
200 | fait |
400 | document malformé, identifiant invalide, ou corps non-UTF-8 |
401 | jeton manquant ou faux |
404 | document ou endpoint inexistant ; rien ne se résout pour cet identifiant |
409 | l’écriture aurait cassé le jeu de réponses (annulée), ou un resolve qui n’a pas pu rendre |
413 | document de plus de 256 Ko |
429 | cette adresse est bloquée ; Retry-After dit pour combien de temps |
500 | le store n’a pas pu être lu ou écrit |
Chaque réponse d’administration définit Connection: close. Un GET /resolve réussi définit
aussi X-Answer-Source, portant la même description que la ligne de log.
Journalisation
Une ligne par requête, sur stderr par défaut. RESCRIPTUM_LOG choisit ce qui est gardé
et RESCRIPTUM_LOG_FILE où cela va — voir
journalisation.
2026-08-24T08:43:37Z 127.0.0.1:61721 POST /answer body=102 200 format=toml machine=98fa9b50d810 group=example-rack bytes=431
Les lignes de niveau serveur portent - là où serait l’adresse du pair. Voir
dépannage.
Formats et alias d'endpoint
Formats et alias d’endpoint
Trois tables. La version narrative est dans un document par système d’exploitation.
Extensions de document
La liste blanche des extensions que rescriptum ramassera dans un store. Tout le reste est
ignoré — txt n’y est délibérément pas, pour qu’un fichier de notes égaré à côté de vos
réponses ne devienne jamais un candidat.
| Extension | Famille | Superposition | Content-Type |
|---|---|---|---|
toml | TOML | fusion structurelle | text/plain; charset=utf-8 |
yaml, yml | YAML | fusion structurelle | text/yaml; charset=utf-8 |
json, ign | JSON | fusion structurelle | application/json |
xml, autoyast, unattend | XML | fusion structurelle, par élément | application/xml; charset=utf-8 |
ks, cfg, preseed, seed, ipxe | texte | concaténation dans l’ordre des couches | text/plain; charset=utf-8 |
La famille est ce que remonte le champ format= de la ligne de log, donc ks et
preseed apparaissent tous deux en format=text. L’extension est ce sur quoi un endpoint
filtre, et ce dont check a besoin pour choisir le bon validateur.
Alias d’endpoint
Un segment de chemin nommant l’un de ceux-ci restreint la réponse aux documents portant les
extensions listées. N’importe quel segment du chemin peut le nommer, donc /rhel/ks,
/ks et /provision/rhel/node.cfg restreignent tous au kickstart.
| Segment | Sert | Usage typique |
|---|---|---|
proxmox, pve, toml | .toml | Proxmox VE |
debian, preseed | .preseed, .seed | preseed Debian |
rhel, centos, fedora, alma, rocky, kickstart, ks | .ks | kickstart |
ubuntu, autoinstall, cloudinit, nocloud, yaml, yml | .yaml, .yml | autoinstall Ubuntu, cloud-init |
flatcar, coreos, ignition, ign | .ign, .json | Ignition |
suse, opensuse, autoyast | .autoyast, .xml | AutoYaST |
windows, unattend | .unattend, .xml | unattend.xml Windows |
json | .json, .ign | |
xml | .xml | |
cfg | .cfg | |
ipxe | .ipxe |
Un segment n’en nommant aucun ne contraint rien, ce qui est pourquoi /answer continue
de fonctionner exactement comme avant.
Deux pièges dans cette table
- Le filtrage porte sur l’extension, pas sur la famille.
.kset.preseedsont tous deux des documents texte ; filtrer par famille laisserait un preseed répondre à/rhel/ks. seedn’est délibérément pas un alias.s=http://server/seed/est une URL de seed NoCloud parfaitement ordinaire, et elle sert du YAML. Un alias doit être assez spécifique pour que personne ne l’atteigne par accident. (L’extension.seedexiste toujours, et/debian/la sert.)
Clés de contrôle, par format
Retirées avant que la réponse ne soit envoyée.
| Format | Écriture |
|---|---|
| TOML | extends = "base", members = […], table [match], au premier niveau |
| YAML | extends:, members:, match: au premier niveau |
| JSON | "extends", "members", "match" au premier niveau |
| XML | <answer-meta extends="base"><member>…</member><match k="v" /></answer-meta> |
| Texte | # answer: extends <nom> · # answer: member a, b · # answer: match k=v k2=v2 |
Les directives texte acceptent aussi // comme marqueur de commentaire. match prend des
paires clé=motif séparées par des espaces ; member une liste séparée par des virgules.
Les commentaires ordinaires d’un document texte sont servis — seules les lignes
# answer: sont retirées.
Sémantique de fusion
| Formats structurés | Formats texte | |
|---|---|---|
| Maps / objets / éléments | fusionnent récursivement | — |
| Scalaires | la couche supérieure remplace | — |
| Tableaux / listes | remplacent, jamais de concaténation | — |
| Document entier | — | concaténé dans l’ordre des couches |
XML apparie les frères par nom d’élément plus un attribut discriminant — name, id, key,
alias, pass — et respecte config:type="list". Déclarations, doctypes, espaces de noms et
attributs survivent à une fusion ; l’indentation d’origine et le placement des commentaires,
non.
Validateurs que check peut appeler
| Format | Outil | Invoqué comme |
|---|---|---|
toml | proxmox-auto-install-assistant | validate-answer <fichier> |
xml, autoyast, unattend | xmllint | --noout <fichier> |
ks | ksvalidator | <fichier> |
| tout le reste | — | aucun n’existe |
Un outil absent du PATH est signalé une fois comme note, jamais comme un échec.
Ligne de commande
Ligne de commande
Sans argument, rescriptum lance le serveur. Tout le reste est une sous-commande.
| Commande | Rôle |
|---|---|
rescriptum | lancer le serveur |
rescriptum render <id> | afficher la réponse que cet identifiant recevrait |
rescriptum render --body FICHIER | …pour un corps de requête capturé |
rescriptum render --query Q | …pour des étiquettes, p. ex. "mac=aa:bb&serial=7ABC1" |
rescriptum check | rendre tout le store configuré et signaler ce qui casse |
rescriptum import <dir> | copier un répertoire de documents dans le store configuré |
rescriptum export <dir> | écrire le store configuré comme un répertoire de documents |
rescriptum migrate [<dir>] | montrer ce que deviendrait un répertoire de réponses plat |
rescriptum migrate --apply | déplacer ces documents dans un répertoire chacun |
rescriptum config | afficher la configuration, et d’où vient chaque valeur |
rescriptum config --json | la même chose, pour un panneau de réglages |
rescriptum config --value CLÉ | une valeur, pour un script — jamais un identifiant |
rescriptum config set C=V … | éditer le fichier que RESCRIPTUM_CONFIG ou RESCRIPTUM_ENV_FILE nomme |
rescriptum config unset CLÉ … | y retirer un réglage |
rescriptum --help | usage et variables d’environnement |
Toutes lisent les mêmes variables d’environnement, dont
RESCRIPTUM_CONFIG et
RESCRIPTUM_ENV_FILE — résolus en premier,
donc un fichier illisible arrête toute commande ayant besoin de la configuration. --help
et --version répondent avant sa lecture, parce que ce sont les commandes qu’on lance
quand quelque chose ne va pas. Il n’y a pas d’options globales.
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
| Forme | Faits fournis |
|---|---|
<id> | l’identifiant comme botte de foin, et rien d’autre — assez pour correspondre par nom, pas assez pour un sélecteur sur serial |
--query "k=v&k2=v2" | ces étiquettes, décodées. path= fournit aussi file et segment, et contraint le format comme le ferait une vraie URL |
--body FICHIER | le fichier verbatim : botte de foin, plus le JSON aplati s’il parse comme du JSON |
- Le document part sur stdout ; la ligne
# format=… machine=… group=…expliquant comment il a été obtenu part sur stderr. Doncrender … > answer.tomlne donne que le document. - Les problèmes de chargement sont d’abord affichés comme lignes
warning:. - Sortie 0 quand quelque chose s’est résolu, 1 quand rien ne s’appliquait (le serveur
aurait renvoyé un
404) ou que le rendu a échoué.
check
$ rescriptum check
Signale les problèmes de chargement, rend chaque document machine et chaque membre de groupe,
nomme les groupes qui sélectionnent sur un bloc match (qu’il ne peut pas essayer sans vraie
requête), et appelle le validateur de l’installateur là où il est dans le PATH.
Sortie 0 quand tout se rend, 1 dès que quelque chose a échoué — il tombe donc tel quel dans une CI. Voir validation.
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 lit un répertoire et écrit dans le store configuré ; export fait l’inverse.
L’aller-retour est identique octet pour octet, chemins compris. Aucun des deux ne lance
check pour vous — la sortie vous le dit.
migrate
Les réponses étaient des fichiers à la racine du répertoire de réponses — 98fa9b50d810.toml
à côté de 98fa9b50d810.preseed. Elles ont désormais un répertoire chacune, et un document
resté à plat est signalé et non servi. Cette commande les déplace :
$ 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.
Elle montre par défaut et ne déplace que si on le lui demande. Le répertoire de réponses est ce à partir de quoi une baie s’installe ; taper la commande pour savoir ce qu’elle ferait ne doit pas le réorganiser.
--apply effectue les déplacements, chacun un rename dans le même répertoire, si bien
qu’aucun document n’est jamais réécrit. Si une destination est déjà prise, rien ne bouge du
tout — y compris les documents qui auraient pu — et les collisions sont nommées : un
répertoire à moitié migré est l’état sur lequel personne ne peut raisonner. Elle prend un
répertoire en argument, RESCRIPTUM_ANSWERS_DIR par défaut, et sur un répertoire déjà migré
elle dit qu’il n’y a rien à déplacer.
config
La configuration, ce sont des variables d’environnement, et sur un déploiement qui les lit depuis un fichier — une installation par paquet, surtout — voici comment les voir et les changer sans ouvrir d’éditeur. C’est aussi ce que l’application DSM exécute dessous.
$ 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
La troisième colonne est l’essentiel. Les fichiers fournissent des valeurs par défaut
et l’environnement réel l’emporte : une valeur marquée environment ne peut donc pas être
changée en éditant un fichier — et config set le dit, plutôt que de vous laisser écrire
quelque chose que le serveur en cours continuera d’ignorer. Avec un fichier TOML la colonne
affiche toml file, et nommer les deux fichiers affiche les deux chemins ainsi que l’ordre
dans lequel ils l’emportent.
config set écrit dans le fichier TOML quand les deux sont nommés, parce que c’est
celui que le serveur lit en premier : écrire l’autre serait une modification qui ne change
rien en silence.
Un identifiant n’est jamais affiché, sous aucune forme de cette commande. Un jeton
apparaît comme (set) ou (not set) ; --value refuse tout net.
$ rescriptum config set RESCRIPTUM_LOG=problems RESCRIPTUM_CAPTURE_DIR=/srv/captures
wrote /var/packages/rescriptum/etc/rescriptum.env
L’écriture laisse le fichier tel qu’il est par ailleurs : les commentaires restent, un
réglage est remplacé là où il se trouve, et un réglage commenté est décommenté sur place
plutôt qu’ajouté en dessous — ce qui compte quand le commentaire au-dessus est la seule
documentation qu’a le fichier. Dans un fichier TOML, le même soin s’applique au document :
la valeur est remplacée là où elle est, son commentaire de fin de ligne survit, et config unset vide la valeur au lieu de supprimer la ligne, pour que le paragraphe qui
explique le réglage reste en place.
Deux refus sont délibérés :
- Une modification qui laisserait un serveur incapable de démarrer est refusée, en bloc, avant toute écriture. Activer l’API d’administration sans jeton, ou avec un jeton de moins de 16 caractères, vous vaut la raison plutôt qu’un prochain démarrage cassé.
- Une variable mal orthographiée est refusée. Écrite, elle serait relue comme une inconnue et signalée au démarrage suivant, quand plus personne ne fait le lien.
Contrairement à toutes les autres sous-commandes, celle-ci fonctionne quand la configuration est trop cassée pour démarrer un serveur — un fichier qui ne parse pas, un jeton d’un caractère trop court. C’est l’état dont on se sert d’elle pour sortir.
media
Les médias de démarrage : les images d’installation que ce serveur détient. Chacune de
ces commandes exige RESCRIPTUM_MEDIA_DIR ; sans elle, elles le disent et sortent en
1. Voir Servir les médias de démarrage.
$ rescriptum media list # ce qui est détenu : famille, architecture, version, empreinte
$ rescriptum media add FILE [--sha256 D] # enregistrer une image déjà dans le répertoire
$ rescriptum media add URL --sha256 D # la récupérer dedans, puis l'enregistrer
$ rescriptum media check # revérifier chaque empreinte enregistrée
$ rescriptum media ipxe ID # imprimer la réponse .ipxe qui démarre une image
$ rescriptum media prepare ID [--url URL] # une image Proxmox avec son URL de réponse dedans
$ rescriptum media export ID FICHIER # matérialiser une entrée préparée, pour une clé
media add prend un fichier déjà dans le répertoire de médias — rien n’est
téléchargé et rien n’est copié. Il le hache avec une progression, l’analyse, et écrit un
fichier compagnon .media à côté. --sha256 est vérifié avant tout enregistrement :
un écart n’écrit rien et sort en 1.
Le code de sortie de media check est un contrat, comme celui de check. deploy.sh
s’y fie.
media ipxe imprime sur stdout et met tout le reste sur stderr, de sorte que
rescriptum media ipxe pve-8.4 > groups/rack-a/boot.ipxe produit un document de réponse
utilisable — ce qu’il est, rien de plus. Il imprime un script, il n’en installe pas.
boot
La moitié démarrage réseau : les chargeurs de TFTP, la configuration DHCP générée, et les deux scripts qu’une machine exécute. Voir Démarrer une machine par le réseau.
$ rescriptum boot dhcp-snippet [--format F] [--one-loader]
$ rescriptum boot check # les chargeurs qu'un extrait nomme sont-ils sur le disque ?
$ rescriptum boot bootstrap # imprimer le script de l'étape deux
$ rescriptum boot menu # imprimer le menu intégré
--format vaut dnsmasq (par défaut), isc, kea, powershell, pfsense ou
mikrotik. L’extrait part sur stdout et les avertissements sur stderr, de sorte que
boot dhcp-snippet > dhcpd.conf produit un fichier incluable tel quel.
Le code de sortie de boot check est un contrat, comme celui de check. Ce qu’il
attrape est la panne la moins diagnosticable de la chaîne : un extrait nommant un
chargeur absent du disque échoue silencieusement au niveau de la ROM, sans rien sur
aucune console. Il signale aussi que le listener média a quitté le port que les chargeurs
distribués ont gravé.
boot bootstrap et boot menu impriment ce qu’une machine exécutera, pour la même
raison que render imprime une réponse : tout ce qu’une baie exécute devrait d’abord
être lisible par un humain.
Codes de sortie
| Code | Signifie |
|---|---|
0 | succès |
1 | la commande a échoué — rien ne s’est résolu, un document ne parse pas, le store n’a pas pu être ouvert |
config est la seule à avoir un second sens : 0 dit que la configuration en est une sur
laquelle le serveur démarrerait, 1 qu’elle ne l’est pas — ou qu’une écriture a été
refusée. Cela la rend utilisable depuis un script, comme check.
Le serveur lui-même sort en 0 sur SIGTERM ou Ctrl-C, et en 1 s’il ne peut pas binder ou
ouvrir le store.