Command reference
Every proofroomctl command and every option of the installer, install.sh: what it does, its options and their defaults, exit codes and examples. The same text is on the server: sudo proofroomctl help [command] and man proofroomctl.
proofroomctl
proofroomctl looks after the Proofroom stacks on one server: each client’s own containers, database, files, keys, domain and licence. Run it with sudo (or as root) on Ubuntu 24.04 or Debian 12 with Docker (and its compose plugin), curl and openssl. The commands below leave sudo out.
It touches only proofroom-* containers, networks and volumes, its root folder /srv/proofroom, /etc/caddy/proofroom.d/, its manual page, and the units and scripts of the daily patch report and updates by button (/usr/local/lib/proofroom/ and proofroom-* units in /etc/systemd/system/). Other applications on the server are left alone.
One changing command runs at a time on a server: a second one stops at once and says so.
sudo proofroomctl [-s | --simulate | --dry-run] COMMAND [ARGUMENTS] [OPTIONS]
| Command | What it does |
|---|---|
help |
List the commands, or describe one. |
version |
Print proofroomctl’s version. |
init |
Prepare this server for stacks. |
create |
Start a new client stack. |
admin-link |
Print a new one-time setup link for an admin. |
update |
Move a stack to another release, with a backup and a rollback. |
resize |
Change a stack’s memory limits, with a restart. |
images |
List the Proofroom images on this server, or remove the ones no stack needs. |
backup |
Take a verified backup now. |
restore |
Replace a stack’s data with a backup, or start a new stack from one. |
backup-decrypt |
Decrypt an off-server copy downloaded from the client’s bucket. |
list |
Every stack, in one table. |
status |
One stack in detail, and the server’s patch report. |
logs |
The app’s logs. |
restart |
Restart the app, after changing its settings. |
licence |
Show the stack’s licence, or install a licence file. |
usage |
Licensed and active users per stack. |
remove |
Delete a stack, keeping a final export, its keys and its backups. |
check-update |
Run by systemd: check for a newer release. Not run by hand. |
apply-update |
Run by systemd: Update now for stack NAME. Not run by hand. |
refresh-units |
Run by update: the release’s units. Not run by hand. |
Options before the command
| Option | What it does |
|---|---|
-s, --simulate, --dry-run |
Simulate: print exactly what the command would do, as numbered steps, and change nothing. Read-only checks still run for real (disk, memory, DNS, the licence file, versions, the server’s lock), so the plan is the one for this server. Secrets are masked. The exit code is the one the command would end with. Put it before the command. |
-h, --help |
The list of commands; after a command (proofroomctl create –help), that command’s help. |
--version |
proofroomctl’s version (also: proofroomctl version). |
Simulate mode
proofroomctl -s COMMAND … (or –simulate, or –dry-run as in install.sh) prints what COMMAND would do on this server without doing it. Each change it would make (a file written or removed, a systemd unit, a Docker container, image or volume, the database, a Caddy site, a call to the licence server that changes something) is printed as a numbered step, in order. Values that are secrets (passwords, keys, tokens, install secrets) are shown as ****.
Checks that only read run for real: the operating system and Docker, free memory and disk, where the domain points, the licence file (checked with the release image, offline), the stack’s and the release’s versions, and whether another proofroomctl command holds the server’s lock. A check that fails ends the simulation with the same message and exit code as the command itself.
The last line says how many steps the command would take and whether it would succeed. Nothing is changed: no file under /srv/proofroom, /etc or /usr/local, no unit, no container and no database row. The step count is kept in one temporary file, removed before proofroomctl exits.
sudo proofroomctl -s create acme --domain compliance.acme.example --licence ~/lic_1.proofroom-licence.json
sudo proofroomctl --simulate update acme --version 1.14
Exit codes
| Code | Meaning |
|---|---|
| 0 | done (simulated: it would succeed) |
| 1 | failed, with a message saying why and what to do (simulated: it would fail) |
| 2 | wrong usage: an unknown command or option, or a missing value |
| 75 | apply-update only: refused because another proofroomctl command holds the server’s lock |
Commands
help
sudo proofroomctl help [COMMAND]
List the commands, or describe one. Without COMMAND: every command, with one line each. With COMMAND: its usage, options with their defaults, exit codes and examples. COMMAND –help does the same. Changes nothing.
Also: -h, --help.
sudo proofroomctl help
sudo proofroomctl help resize
version
sudo proofroomctl version
Print proofroomctl’s version. Prints proofroomctl VERSION. Each release carries its own proofroomctl; a healthy update puts the release’s copy in place.
Also: --version.
sudo proofroomctl version
init
sudo proofroomctl init [--registry R] [--registry-user U] [--registry-token-file F|-] [--own-proxy] [--licence-server URL] [--update-url URL] | init --refresh
Prepare this server for stacks. Checks the server (operating system, Docker, memory), logs in to the registry when given a token, installs Caddy from its own package repository unless –own-proxy, writes /srv/proofroom/ctl.env, and installs the daily patch report (it only reads; it never changes the server), updates by button and this manual page. Run it again at any time: it changes only what is missing or different. If Caddy is already installed, init adds one import line to /etc/caddy/Caddyfile, keeps a copy from before as Caddyfile.before-proofroom, and undoes the change if Caddy refuses it.
| Option | Default | What it does |
|---|---|---|
--refresh |
On a server init already prepared: install or refresh the patch report, updates by button and the manual page, and nothing else. Takes no other option. | |
--registry R |
ghcr.io/proofroom-team/proofroom | The image repository the release images come from. |
--registry-user U |
proofroom | The user for –registry-token-file. |
--registry-token-file F |
Log in to the registry with the token in file F; - reads it from standard input. The token never appears on a command line. | |
--own-proxy |
Do not install or touch Caddy. For each stack, proxy its domain to http://127.0.0.1:PORT (create prints it) with a request body limit of at least 110 MB, and terminate TLS yourself. | |
--licence-server URL |
https://licence.proofroom.com | Where stacks check in. “” for a server without internet access: check-ins are then off. |
--update-url URL |
https://proofroom.com/releases/latest.json | Where root reads the newest release’s version every 4 hours. “” turns the check, the dashboard notice and Update now off on this server. |
sudo proofroomctl init
sudo proofroomctl -s init --own-proxy
sudo proofroomctl init --refresh
create
sudo proofroomctl create NAME --domain D --licence FILE [--version V] [--admin-email E] [--app-memory M] [--db-memory M] [--master-key-from APP_ENV]
Start a new client stack. Checks the licence file (signature and domain) with the release’s own image, generates the stack’s passwords, AUTH_SECRET and MASTER_KEY, picks a port from 41000, starts the stack, waits until it is healthy, adds the Caddy site and, with –admin-email, prints the first admin’s one-time setup link (valid 24 hours). A firm licence file first registers the site with the licence server, which gives it its own install secret. NAME is 2 to 31 lower-case letters, digits or dashes, starting with a letter. The domain needs a DNS record pointing at this server first: Caddy asks for the certificate as soon as the site is added.
| Option | Default | What it does |
|---|---|---|
--domain D |
The client’s domain, as named on the licence. Required. | |
--licence FILE |
The licence file from the licence server. Required. | |
--version V |
proofroomctl’s own version | The release to run. |
--admin-email E |
Create the first admin and print their one-time setup link. | |
--app-memory M |
1g | The app’s memory limit, like 1g or 768m (resize changes it later). |
--db-memory M |
512m | The database’s memory limit, like 512m or 1g (resize changes it later). |
--master-key-from APP_ENV |
Take MASTER_KEY from another stack’s app.env, to restore its backups here. |
sudo proofroomctl -s create acme --domain compliance.acme.example --licence ~/lic_1.proofroom-licence.json
sudo proofroomctl create acme --domain compliance.acme.example --licence ~/lic_1.proofroom-licence.json --admin-email it@acme.example
admin-link
sudo proofroomctl admin-link NAME --email E [--name "FULL NAME"]
Print a new one-time setup link for an admin. Creates a local admin, or recovers an existing one (a lost or expired link, a lost authenticator), and prints a new one-time setup link, valid 24 hours. Send it securely.
| Option | Default | What it does |
|---|---|---|
--email E |
The admin’s email. Required. | |
--name "FULL NAME" |
The person’s display name, on a new or an existing account. |
sudo proofroomctl admin-link acme --email it@acme.example --name "Jo Smith"
update
sudo proofroomctl update NAME [--version V]
Move a stack to another release, with a backup and a rollback. Pulls the release, takes a backup, moves the stack to it and waits until it is healthy. If it does not come up, the stack goes back to the release it ran and the data from that backup. After a healthy update, proofroomctl puts the release’s own proofroomctl, units and manual page in place, and removes the Proofroom images no stack needs (each stack keeps the image it runs and the one before it; see images). Before anything changes, it stops when the disk is clearly short of the new image’s download (its size in the registry) and 512 MB, after removing old Proofroom images no stack uses if that makes the room; when the size can’t be read, it says so and goes on. Copy the backup and app.env off the server before relying on the update: the backup is on the same server.
| Option | Default | What it does |
|---|---|---|
--version V |
proofroomctl’s own version | The release to move to. X.Y moves to the newest X.Y.Z. |
sudo proofroomctl -s update acme --version 1.14
sudo proofroomctl update acme
resize
sudo proofroomctl resize NAME [--app-memory M] [--db-memory M]
Change a stack’s memory limits, with a restart. Sets the app’s and the database’s memory limits (and the app’s heap, three quarters of its limit) and restarts the stack’s containers on them, which takes about a minute. It refuses, changing nothing, when the extra memory asked for is more than this server has free (MemAvailable, less 256 MB kept for the system). If the stack does not come back healthy, the previous limits are put back. Give at least one of the options; the app needs at least 512m and the database at least 256m.
| Option | Default | What it does |
|---|---|---|
--app-memory M |
unchanged | The app’s new memory limit, like 2g or 1536m. |
--db-memory M |
unchanged | The database’s new memory limit, like 1g or 768m. |
sudo proofroomctl -s resize acme --app-memory 2g --db-memory 1g
sudo proofroomctl resize acme --app-memory 2g
images
sudo proofroomctl images [--clean]
List the Proofroom images on this server, or remove the ones no stack needs. Lists the Proofroom images on this server (the release repository, the pre-1.1.1 name ghcr.io/hriday/proofroom and the server’s own registry) with their sizes, and whether each is kept: every stack keeps the image it runs and the one before it, for a rollback. With –clean, removes the others one by one, by tag or by ID; an image Docker will not remove (a container still uses it) is left with a note. Never removes images of other repositories (the database’s, anything else on the server) and never prunes. update does the same after a healthy update.
| Option | Default | What it does |
|---|---|---|
--clean |
Remove the Proofroom images no stack needs. |
sudo proofroomctl images
sudo proofroomctl -s images --clean
sudo proofroomctl images --clean
backup
sudo proofroomctl backup NAME
Take a verified backup now. Takes a backup into /srv/proofroom/stacks/NAME/backups/ (the database, the evidence files and a manifest), checks it, prints its folder and keeps the newest 14. The app also takes one every night, and can copy it to the client’s own S3-compatible bucket (Admin → Backups). A backup needs the stack’s MASTER_KEY (in app.env) to be restored: keep a copy of app.env off the server too.
sudo proofroomctl backup acme
restore
sudo proofroomctl restore NAME BACKUP [--yes] | restore SOURCE BACKUP --into NEW --domain D --licence FILE
Replace a stack’s data with a backup, or start a new stack from one. BACKUP is a name in the stack’s backups/ or a folder: a backup, or an off-server copy downloaded from the client’s bucket, which is decrypted first with the stack’s MASTER_KEY (as backup-decrypt does) into /srv/proofroom/restored/. Restoring into the same stack asks you to type its name (or –yes), takes a safety backup first, stops the app, restores and starts it again; if the restore fails, the stack keeps the data it had. With –into, a new stack NEW is created with SOURCE’s version and MASTER_KEY, and the backup is restored into it. On a firm licence, restoring another site’s backup counts as an import.
| Option | Default | What it does |
|---|---|---|
--yes |
Do not ask for the stack’s name. | |
--into NEW |
Start a new stack named NEW from the backup instead. | |
--domain D |
With –into: the new stack’s domain. | |
--licence FILE |
With –into: the new stack’s licence file. |
sudo proofroomctl restore acme 20261021T013000Z
sudo proofroomctl restore acme /mnt/copy/20261021T013000Z --into acme2 --domain new.acme.example --licence ~/lic_2.proofroom-licence.json
sudo proofroomctl restore acme /root/copy/20261029T013000Z
backup-decrypt
sudo proofroomctl backup-decrypt SOURCE --master-key-from APP_ENV [--out DIR] [--version V]
Decrypt an off-server copy downloaded from the client’s bucket. SOURCE is one backup’s folder as downloaded from the bucket (Admin → Backups uploads each nightly backup there, encrypted with a key derived from the stack’s MASTER_KEY): the .enc files and copy.json. It checks every file’s SHA-256 and the key against copy.json, decrypts them with the release image, offline, and writes DIR/BACKUP: a backup that restore takes, with the stack’s licence file (licence.proofroom-licence.json) beside it for create on a new server. Nothing is written unless the whole copy decrypts. The key comes from APP_ENV, the stack’s app.env you kept off the server; it never appears on a command line.
| Option | Default | What it does |
|---|---|---|
--master-key-from APP_ENV |
The app.env holding the stack’s MASTER_KEY. Required. | |
--out DIR |
/srv/proofroom/restored | Where the decrypted backup’s folder goes. |
--version V |
proofroomctl’s own version | The release image that decrypts it (1.14.2 or later). |
sudo proofroomctl backup-decrypt /root/copy/20261029T013000Z --master-key-from /root/acme-app.env
sudo proofroomctl restore acme /srv/proofroom/restored/20261029T013000Z
list
sudo proofroomctl list
Every stack, in one table. For each stack: version, domain, port, health, licence state and last backup. Changes nothing.
sudo proofroomctl list
status
sudo proofroomctl status [NAME]
One stack in detail, and the server’s patch report. Image, address, health, last backup, licence and containers of stack NAME, then this server’s patch report with each warning and the command that fixes it. Without NAME, only the patch report. Changes nothing.
sudo proofroomctl status acme
sudo proofroomctl status
logs
sudo proofroomctl logs NAME [--since D] [--follow]
The app’s logs. The last 200 lines of the app’s log. Changes nothing.
| Option | Default | What it does |
|---|---|---|
--since D |
Only lines since D: a time, or a duration like 1h or 30m. | |
--follow, -f |
Keep printing new lines until you press Ctrl-C. |
sudo proofroomctl logs acme --since 1h
sudo proofroomctl logs acme -f
restart
sudo proofroomctl restart NAME
Restart the app, after changing its settings. Starts a new app container, so it reads /srv/proofroom/stacks/NAME/app.env again (single sign-on, email and the other settings), and waits until it is healthy.
sudo proofroomctl restart acme
licence
sudo proofroomctl licence NAME [FILE]
Show the stack’s licence, or install a licence file. Without FILE: the licence’s state, users and expiry. With FILE: checks it with the stack’s image, installs it and restarts the app. For renewals by hand, stacks without internet access, and moving a site onto a firm licence.
sudo proofroomctl licence acme
sudo proofroomctl licence acme ~/lic_1.proofroom-licence.json
usage
sudo proofroomctl usage [--csv]
Licensed and active users per stack. For each stack: its domain, licence, licensed users, active users and licence state. Changes nothing.
| Option | Default | What it does |
|---|---|---|
--csv |
Comma-separated values with a header line, for a spreadsheet. |
sudo proofroomctl usage --csv > usage.csv
remove
sudo proofroomctl remove NAME --confirm NAME [--no-export]
Delete a stack, keeping a final export, its keys and its backups. Takes a final export, then copies it, .env, app.env and the backups into /srv/proofroom/removed/NAME-TIME/, and deletes the stack’s containers, volumes, folder and Caddy site. On a firm licence, release the site first (Admin → Licence) or its users keep counting.
| Option | Default | What it does |
|---|---|---|
--confirm NAME |
The stack’s name again. Required. | |
--no-export |
Remove without the final export (for a stack that never started). |
sudo proofroomctl remove acme --confirm acme
check-update
sudo proofroomctl check-update
Run by systemd: check for a newer release. Run every 4 hours by proofroom-update-check.timer: one GET of the latest-release file (nothing is sent), recorded in /srv/proofroom/host/update.json for every stack’s Admin → Updates. Also finishes the status of an update a reboot interrupted. Always exits 0.
sudo proofroomctl -s check-update
apply-update
sudo proofroomctl apply-update NAME [--clear]
Run by systemd: Update now for stack NAME. Run by proofroom-update@NAME.service when an admin presses Update now: takes the request, refuses (changing nothing) unless the latest release is newer than the stack and can be reached from it, and otherwise runs update with each phase in the stack’s update status.
| Option | Default | What it does |
|---|---|---|
--clear |
Only take a request and finish the status of a run that stopped (the unit’s stop step). |
Exit code 75: refused: another proofroomctl command holds the server’s lock.
sudo proofroomctl -s apply-update acme
refresh-units
sudo proofroomctl refresh-units IMAGE
Run by update: the release’s units. Run by update with the release image it has just moved a stack to: installs or refreshes the patch report, updates by button and the manual page as that release writes them, like init –refresh.
sudo proofroomctl -s refresh-units ghcr.io/proofroom-team/proofroom:1.14.2
Files
| Path | What it is |
|---|---|
/srv/proofroom/ctl.env |
the server’s settings, written by init: registry, proxy, licence server, update address |
/srv/proofroom/stacks/NAME/ |
a stack: compose.yml (from the release), .env (image, port, memory, database passwords), app.env (the app’s settings, including MASTER_KEY), licence/, backups/ |
/srv/proofroom/removed/ |
what remove keeps: the final export, the keys and the backups |
/srv/proofroom/host/ |
the daily patch report and the latest-release check, read by every stack |
/etc/caddy/proofroom.d/NAME.caddy |
a stack’s site in Caddy (not with init –own-proxy) |
/usr/local/lib/proofroom/ |
root’s copy of proofroomctl and the patch report script, run by systemd |
/usr/local/share/man/man8/proofroomctl.8 |
this manual page |
install.sh
The installer: install Proofroom on this server with one command.
install.sh --code CODE [OPTIONS]
install.sh --licence FILE --email EMAIL [OPTIONS]
install.sh checks the server and stops at the first problem, saying what to do; fetches the licence with the install code (and saves it, so running it again needs no code); installs Docker if it is missing, then proofroomctl; runs proofroomctl init and create; and prints the first admin’s one-time setup link. It is safe to run twice. It sends the install code to the licence server and nothing else, and never asks for a password or a key.
Each release’s installer is also published as https://proofroom.com/install-X.Y.Z.sh with its SHA-256 in install-X.Y.Z.sh.sha256. Those files never change; https://proofroom.com/install.sh is always the newest.
| Option | Default | What it does |
|---|---|---|
--code CODE |
The install code from your licence email or download page. It works once. | |
--licence FILE |
A licence file you copied to this server, instead of a code. | |
--email EMAIL |
The first admin’s email (with –licence; a code already names it). | |
--name NAME |
from your company’s name | The stack’s name. |
--yes |
Do not ask before installing (needed when there is no terminal, as under cloud-init). | |
--dry-run |
Check the server and print the commands; nothing is changed and the code is not used. | |
--skip-dns-check |
For a server behind a proxy or NAT: the HTTPS certificate then fails unless that proxy sends the domain’s traffic here. | |
--help, -h |
The options. |
| Exit code | Meaning |
|---|---|
| 0 | installed, or the stack was already there and is healthy (dry run: the checks passed) |
| 1 | a check or a step failed; the message says what to do |
| 2 | wrong usage |
curl -fsSL https://proofroom.com/install.sh | sudo bash -s -- --code XXXX-XXXX-XXXX
sudo bash install-X.Y.Z.sh --licence /root/compliance.acme.example.proofroom-licence.json --email it@acme.example --dry-run