rescriptum · an answer written for this machine
EN
On this page

Deployment

The binary is self-contained: copy it somewhere, give it an answers directory, and start it. Everything below is about doing that repeatably.

For Synology DSM 7 — which has no systemd — see its own page.

An environment file

Keep configuration in one root-readable file rather than in a unit or a command line. Anything on a command line is visible to every user on the machine through ps, which matters as soon as a token is involved:

# /etc/rescriptum.env   (chmod 600, owned by root)
RESCRIPTUM_ANSWERS_DIR=/srv/answers
RESCRIPTUM_LISTEN_ADDR=0.0.0.0:8000
RESCRIPTUM_TIMEOUT_SECS=10
# RESCRIPTUM_ANSWER_TOKEN=…

Under systemd, EnvironmentFile= below reads it and you need nothing else. Elsewhere — and on DSM 7, which has no systemd — point RESCRIPTUM_ENV_FILE at the same file and the binary reads it itself, refusing to start if it cannot.

A systemd unit

# /etc/systemd/system/rescriptum.service
[Unit]
Description=rescriptum — per-machine answer files for unattended installs
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/local/bin/rescriptum
EnvironmentFile=/etc/rescriptum.env
Restart=on-failure
RestartSec=2

# It needs to read one directory and bind one port. Nothing else.
DynamicUser=yes
ReadOnlyPaths=/srv/answers
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=yes
ProtectKernelTunables=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6
SystemCallFilter=@system-service

[Install]
WantedBy=multi-user.target
$ sudo systemctl enable --now rescriptum
$ curl -s http://localhost:8000/health
OK

Adjust for what you actually enable:

  • SQLite store — the database needs to be writable, so ReadWritePaths=/srv and drop ReadOnlyPaths.
  • Request captureReadWritePaths= the capture directory.
  • A port below 1024 — add AmbientCapabilities=CAP_NET_BIND_SERVICE.

Logs go to stderr, so journalctl -u rescriptum -f is the live view.

In a container

There is nothing to install, so the image is the binary:

FROM scratch
COPY rescriptum /rescriptum
ENV RESCRIPTUM_ANSWERS_DIR=/answers RESCRIPTUM_LISTEN_ADDR=0.0.0.0:8000
EXPOSE 8000
ENTRYPOINT ["/rescriptum"]

Use the build for the right architecture — the musl ones are statically linked, which is what makes FROM scratch work. Mount the answers directory read-only.

Sizing it

The defaults are already right for both ends of the range this was built for.

SettingDefaultChange it when
RESCRIPTUM_WORKERSCPU countyou are sharing a small box and want to cap runtime threads
RESCRIPTUM_MAX_CONNECTIONS2048you are seeing 503s during a burst — or want to shed earlier
RESCRIPTUM_TIMEOUT_SECS10clients are on a slow link, or you want to cut off slowloris sooner

MAX_CONNECTIONS is not a throughput limit. Over the cap the server writes a prompt 503 and closes rather than queueing — a client that is told to retry is better off than one parked in a queue that turns a burst into an out-of-memory.

A 2,000-machine rollout completes in under two seconds at the measured throughput, so sizing is rarely the interesting problem. Troubleshooting usually is.

Replacing a running instance

$ ./deploy.sh admin@nas
$ ./deploy.sh admin@nas /volume1/netboot        # a different remote directory

What it does, in order:

  1. Builds for the target (TARGET, default armv7-unknown-linux-gnueabihf).
  2. Checks the local answers with rescriptum check and refuses to continue if anything fails — shipping a broken answer set is worse than not deploying.
  3. Copies the binary under a temporary name, then renames it into place. Replacing a running binary in place is how a half-copied file gets executed.
  4. Stops the running instance, starts the new one detached, and confirms it stayed up.
  5. Confirms /health answers over the network, so a firewall problem is reported as one rather than as a mysterious silence.
EnvironmentDefault
TARGETarmv7-unknown-linux-gnueabihf
ANSWERS<remote-dir>/answers
PORT8000

It replaces what is running; it does not install autostart. On DSM that is a Task Scheduler entry; with systemd it is systemctl enable.

Upgrading

Answers are data, not state: nothing is migrated, and a new binary reads the same directory. Replace it and restart.

The exception is the SQLite store, which carries a schema version. There is one so far, so there is nothing to migrate; what the version buys is the other direction, an older binary refusing to open a database written by a newer one rather than guessing at it. See the SQLite store.

Updated Edit this page