CLI Reference — sre

sre --help groups subcommands into five buckets: dual (admins + GUI via sre-wrapper), admin-only, exam management, post-exam management, and internal (GUI-only).

Global options

sre [--user] [--debug] <subcommand> ...

Option

Description

--user

Set by sre-wrapper; reads student login from USER_USERNAME (else LOGNAME). Mutually exclusive with --debug. In user mode a command only reaches the caller’s own projects and those started by root or the sre user (sudo sre start …); with params.check_project_owner = False every project of the host, as before 0.14.

--debug

Verbose diagnostics to stderr.

Access is restricted to root, the sre user (params.sre_uid = 1100), members of params.admin_uids, or members of any params.admin_gids group. Non-privileged users may only run cat, check-eval, re-eval, sheet, and outline.


Dual commands

sre start [-d data] [-p] [--flavor … | --flavor-json … | --set-flavor-name …] [--xauth-file <file>] [--instructor-mode] <lab> [data_version]

Starts a new lab instance, named {timestamp}@@@{lab_name}@@@{username}. Imports srelab.py, runs Data.generate(flavor) (or loads -d data.json), builds NetScheme, pulls images, deploys via Kathara, applies the initial state, and writes info.json. See Internals for file-transfer and progress mechanics.

Argument / Option

Description

lab

Lab name (from sre list) or, with -p, a filesystem path.

-p / --path

Treat lab as a path.

-d / --data

Reuse an existing data.json instead of generating one.

data_version

Optional seed for Data.generate() (privileged only; incompatible with -d).

--flavor key:val …

Flavor field values (e.g. --flavor nb:3 mode:hard).

--flavor-json <json>

Flavor as JSON dict (used by the GUI).

--set-flavor-name <name>

Use a named preset Flavor (privileged only).

--xauth-file <file>

Read SRE_XAUTH_COOKIE from an X authority file instead of the env var (privileged only; for x11_host=True machines).

--instructor-mode

Start the project in instructor mode (privileged only).

--debug-project

Start a debug project (see Lab Authoring; privileged only): lab is read as a path, -p implied.

--debug-web-project

The same debug project, lab read as a lab name as for a normal start (the exam-only labs accepted), so that the project keeps the lab name of the catalog; privileged only, what the web agent runs; incompatible with -p and --debug-project.

sre stop <running_lab>

Undeploys containers and removes the project directory from /var/lib/sre/projects/. Kathara only undeploys the containers whose user label matches the caller, so stop first aligns SUDO_UID with the label used at deploy time (sre for non-privileged labs, the project owner for privileged ones; both are tried when the lab is not loaded, as in pre-start-exam).

The name may be a unique substring of the running lab name (every command taking a <running_lab> resolves it this way). In user mode only the caller’s own projects and those started by root or the sre user are candidates: another user’s project is refused by its exact name and never listed nor matched by a substring (params.check_project_owner).

sre stop-all

Stops every running project the caller may access, each one as sre stop does (with the lab_hash of its info.json: no lab module is loaded and both container labels are tried): all of them for a privileged caller, in user mode the caller’s own projects and those started by root or the sre user (every project when params.check_project_owner is off). The GUI’s File → Close All Projects runs it. Unlike sre wipe nothing else is touched: no Kathara wipe, the stray entries of the projects directory and the orphan user public directories stay, and a project without info.json (a start in progress or failed) is left alone. Refused in user mode during an exam, like stop.

sre save [-o <file>] [--full-images] <running_lab>

Saves a running project into a single save file (.sre): the filesystem of every container (captured by the patched Kathara save_lab, as a diff against the base image by default) plus the project files (data.json, answers/, .private/files, the user public dir with shared/ and the volume directories, .private/mnt). The lab must set allow_save_restore = True; the lab’s save state is applied first (see Lifecycle states). Only files are captured, not running processes.

Option

Description

-o / --output <file>

Write the save file here instead of stdout (privileged only).

--full-images

Store full committed device images instead of filesystem diffs — self-contained but large (privileged only).

In user mode (--user, i.e. from the GUI) the file is always written to stdout. When the lab defines save_key, the payload is encrypted. See Save file format.

sre restore [--instructor-mode] <save_file | ->

Restores a save file as a new running project (fresh timestamp, current user): loads the lab module named in the header (allow_save_restore required), recreates the project directories and copies the saved project files back, rebuilds the Kathara lab from the restored data.json (fresh host ports, X11 cookie, volumes, /shared), redeploys the containers from the saved filesystems (Kathara.restore_lab(…, lab=…), same pull/deploy progress events as start), refreshes exetests.py, applies the lab’s restore state and writes info.json. On failure everything created so far is rolled back.

- reads the save file from stdin — the only form accepted in user mode (the GUI opens the file as the student). Labs referenced by an absolute path (sre start -p) can only be restored by privileged users, unless the path lies inside the lab directory (/opt/sre/lab), in which case the save file is restored like any other lab of that directory. An encrypted file needs a lab with the same save_key; a cleartext file for a lab that defines save_key is refused in user mode (accepted with a warning otherwise).

--instructor-mode (privileged only) puts the new project in instructor mode. A save file never carries the mode: without the option the restored project is a normal one, whatever the saved project was.

sre wipe

Stops every running Kathara lab and removes everything under /var/lib/sre/projects/ and the user public directories. Privileged only: the user-mode form is sre stop-all.

sre connect [--shell <shell>] [--exec <argument…>] [--no-records] <running_lab> <device>

Opens a terminal session on a device: the shell of a machine, or the management console of a managed switch when <device> is a network declared with 'mode': 'managed' (prompt <network>$ , exit leaves). In user mode the console only accepts the commands of params.switch_user_commands, and it is refused when the lab sets 'allow_connection': False on the switch. The commands of the console are listed in Hubs, switches and VLANs.

Option

Description

--shell <shell>

Override the machine default shell (privileged only; not for a switch).

--exec <argument…>

Run one command via the shell and return (consumes rest of argv). On a switch: one console command, privileged only.

--no-records

Do not record the terminal session (privileged only).

sre eval [-p path] [--auto-eval] <running_lab>

Evaluates a running lab. The atomic lock file .private/eval_in_progress prevents concurrent evals.

--auto-eval marks a user-triggered self-evaluation (sent by the GUI’s Start evaluation). With --user, it enables the delay_between_self_grade cooldown (outside exam mode), appends a line to .private/auto_eval.log, and prints the result JSON. Without --auto-eval, a --user invocation runs silently — no cooldown, no log, no stdout. The auto_eval_count (lines in .private/auto_eval.log before this run) is assigned to self.auto_eval_count and embedded in the archive’s answers. If the lab sets no_mark_on_self_grade, letter grades (OK/MEH/FAIL) replace numeric grades. Results are archived as .zst — see Archive format.

sre state <running_lab> <state_name>

Applies NetScheme.<state_name>() to a running lab. In user mode only the states declared @sre_state(user_allowed=True) of a lab setting allow_user_states = True are accepted, except on a debug project or a project in instructor mode, where every state is. File operations registered via file() / append_to_file() are injected via in-memory tar archives sent to container.put_archive("/").


Admin-only commands

sre exec [--shell <shell>] <running_lab> <device> <command…>

Runs shell -c <command> in a container and forwards stdout/stderr/exit code. Default shell is params.default_exec_shell.

When <device> is a managed switch (a network with 'mode': 'managed'), <command…> is one command of its management console, e.g. sre exec <running_lab> lan vlan/print or sre exec <running_lab> lan port/setvlan @pc1 10 (@machine: the port of that machine). The exit status is 0, or the error number given by the switch (17 when a VLAN already exists) with the message on stderr.

sre set-instructor-mode <running_lab> and sre remove-instructor-mode <running_lab>

Switch the instructor mode of a running project (sre start --instructor-mode and sre restore --instructor-mode set it from the beginning). The mode is meant for the instructor’s own project and has three effects:

  • the texts the lab wraps in instructor() (see Lab Authoring) are kept in info.json; for any other project but a debug project (sre start --debug-project, which keeps them too) they are dropped before the file is written, so students cannot read them;

  • every sre state appends what it executed to the project’s operations.log (evaluations are not logged, unlike in a debug project);

  • every state can be applied, as to a debug project: sre state in user mode (what the GUI runs) no longer requires allow_user_states nor @sre_state(user_allowed=True).

In the GUI all three show only while the Instructor mode button is on (see GUI). Nothing else changes: hidden machines and grades are those of a normal project.

Both commands write info.json again; the question hashes do not depend on the mode, so the answers already given stay attached. remove-instructor-mode also deletes operations.log, unless the project is a debug project (which also keeps its instructor texts).

sre eval-all [--display-grades / --no-display-grades]

Evaluates every running lab instance concurrently. --no-display-grades suppresses the per-lab grade summary.

sre check [-p] <lab> [<state>]

Validates a lab module without deploying: imports it, runs Data.generate(), checks that the Data instance survives the data.json round trip (to_json() / from_json(), pack() / unpack()), builds NetScheme, runs initial(), calls Grade.grade(). With state, also validates that state method. The networks that are not hubs are printed with their type and the VLANs of their ports, and the console commands a state sends to a managed switch are listed with its other operations (switch <network>: cmd …). The numbered sections of informations (info_subtitle() / @@sec@@ markers) are resolved as at sre start and printed as an outline (number, title, tag), and the @@ref@@ references of the question texts are checked: a marker outside a header, a level jump, a tag defined twice or unknown, or languages whose sections differ, is a FAIL.

sre watch [--timeout <sec>] [--interval <sec>] [-H <regexp>] [-S <time>] [-L] <dir…>

Live terminal dashboard scanning <dir…> for .zst archives. Default refresh: params.default_dashboard_refresh_interval_in_watch_command. --timeout is the inactivity-alert threshold (default 90 s). -H/--hostname-filter and -S/--starting-time preset the R and S keys.

One row per running project instance on a host: hostname + lab + the start timestamp of running_lab_name. A lab opened twice by the same student gives two rows with their own grades and alerts; a lab stopped then started again keeps its old row until it is dismissed (P), hidden (S) or collapsed. -L/--only-last-instances (the L key) collapses to one row per host and lab: the most recently started instance, whatever the age of its archives. Dismissals and S apply after the collapse, so they never reveal an older instance. Lab counts and statistics are per instance.

Columns (from each archive):

Column

Source

HOSTNAME / LOGIN

answers["hostname"] / answers["login"]

LAB NAME

Middle segment of running_lab_name, its @ decoded back to / (the lab path given to sre start)

STARTED

First segment of running_lab_name (instance start; time only when today)

GRADE

total_grade / total_max

ERR

Length of errors

LAST EVAL

eval_date (YYYYmmddHHMMSS)

TIME REMAINING

answers["exam_time_remaining"] minus elapsed since answers["answers_updated_at"]

Keys:

Key

Context

Action

t

Any

Toggle focus between Projects and Alerts

↑ / ↓

Projects / Alerts

Move selection

Enter

Projects

On a project row: its grade elements; on a Lab: row: per-element aggregation over the lab’s instances

P

Projects

Dismiss selected project instance (hostname + lab + start)

H

Projects

Dismiss all projects from the selected hostname

R

Projects

Set a hostname regexp filter (empty = show all; initial value: -H)

S

Projects

Only show projects with an archive received after a time (empty = no limit; initial value: -S)

L

Projects

Toggle “only last instances”: one row per host and lab, the most recently started (initial value: -L)

U

Either

Un-dismiss all

d / Enter

Alerts

Dismiss selected alert

q / Ctrl-C

Any

Quit

?

Any

Toggle help

Dismissals self-expire: inactivity alerts return on new archives, error alerts return when the count changes.

sre preload-images [--random-delay <sec>] [--progress] <file_or_dir…>

Scans lab .py files for image= / 'image': patterns and docker pulls each unique image. --random-delay <sec> waits 0–N seconds before pulling (smooths thundering-herd when invoked across many hosts). --progress also writes the steps as JSON lines on stderr ({"phase": "preload", "status": "start", "total": N}, then "status": "pull" with image / current / total before each pull and "status": "error" with image after a failed one); what is printed on stdout and the exit status are unchanged. It is what the agent of the web front-end runs when the portal gives its host a turn to preload (see Pre-loading the images in the pool section of the web front-end).

sre make-titles [-o <file> | -r] [-d] <directory>

Generates titles.json sidecars so the Open project dialog of the GUI and the Open page of the web front-end show friendly labels (e.g. static_routing.py → “Static routing”). Labs without a title attribute are skipped.

Option

Description

-o, --output-file <file>

Write all titles to <file> (incompatible with -r).

-r, --recursive

One titles.json per directory containing labs (incompatible with -o).

-d, --directories

Also list the sub-directories of each directory, with a null title to be written by hand (see below); with -r, the directories holding sub-directories only get their titles.json too.

Output keys match sre list: "<name>.py" for file labs, "<dirname>" for directory labs. A rerun merges into the existing file: its options and the order of its entries are kept (a manual order survives), the labs found now are updated, the entries naming nothing on disk any more are dropped, the new labs are appended by name; a fresh file lists the labs by name. The languages of a title are written sorted, the file atomically. Stale entries are also dropped at read time by sre list --with-titles.

Options and sub-directories (written by hand): every key starting with @ is an option, never an entry. "@sort" sets the order of the entries of the directory in the lab tree, the same in the GUI and on the web: "name" (the default: the file names, byte-wise), "title" (the labels in the language of the user, case and accents ignored, numbers compared as numbers so that “TP 2” comes before “TP 10”; a lab without title sorts by its file name) or "manual" (the order of the entries in the file, then the unlisted ones by name). In every mode the sub-directories come first, then the labs. A directory without the option takes the one of the nearest ancestor (so /opt/sre/lab/titles.json sets the whole tree); an unknown value is ignored and sre make-titles warns about it. A key may also name a sub-directory, with a title (shown instead of its name, in the language of the user) or null (shown by its name; listed for its position in the manual order only); sre make-titles keeps such entries as they are.

{
  "@sort": "manual",
  "s4":               {"en": "Semester 4",      "fr": "Semestre 4"},
  "static_routing.py": {"en": "Static routing", "fr": "Routage statique"},
  "tp_ssh":           null
}

Sub-directory entries written by the command (-d): without the option the command never writes a sub-directory key, it only keeps those already there. With -d, the titles.json of a directory also lists its sub-directories, those of the lab tree (a lab lies somewhere below them; exam-only ones included, like the labs), each with null until a title is written by hand: the keys are there, only the titles are left to type. A sub-directory already listed keeps its entry and its place; the missing ones are added by name after the last sub-directory entry of the file, else before its first entry, so a fresh file lists the sub-directories then the labs, as the tree shows them, and the tree shown is the same before and after in every "@sort" mode. With -r -d every directory of the tree gets its file, including a level holding sub-directories only such as the lab root (/opt/sre/lab/titles.json), which -r alone never visits. The line reporting a written file names the sub-directories still without a title:

wrote 2 title(s) to /opt/sre/lab/titles.json (sub-directories without a title: s4, sre)

Typical usage: sudo /opt/sre/sbin/sre make-titles -r /opt/sre/lab, or with -d to also get the sub-directory entries to title.

sre update [--main | --ref <tag|branch|commit>] [--check | --force] [-y] [--venv]

Updates the installation (params.main_sre_dir, /opt/sre) from the GitHub repository sysreseval/sysreseval (params.update_github_repository). Privileged only, and root only: the tree is root-owned. By default the latest published release is installed. The installed version is the commit recorded by the previous update in <main_sre_dir>/.sre-update.json (or the checked-out commit of a git clone), so an unchanged repository means “already up to date”.

Option

Description

--main

Follow the head of the main branch instead of the latest release.

--ref <tag|branch|commit>

Install this tag, branch or commit, e.g. the previous commit printed by the last update, to go back.

--check

Only report the installed and the available versions; nothing is changed.

--force

Install again even when the requested commit is already installed.

-y, --yes

No question: every customised parameter of params.py is kept and no confirmation is asked (required when stdin is not a terminal).

--venv

Rebuild the Python virtual environment (make venv, a few minutes) as the last step.

What an update does, in order:

  1. Checks: no exam configured (sre del-exam first), a writable tree (on a read-only NFS mount, run it on the server), one update at a time.

  2. Downloads the source archive of the commit into a temporary directory of the tree and decides everything before touching anything: which files change, which are deleted, what params.py becomes, and that the new code compiles with the venv’s Python.

  3. Prints a summary and asks for a confirmation (unless --yes).

  4. Runs sre wipe: every running project is stopped and removed, since the new code may not handle projects started by the old one. Without a Docker daemon on the host (params.docker_socket_path missing, e.g. an NFS server), the wipe is skipped with a warning: run sre wipe on the client hosts.

  5. Replaces the files and reapplies the ownership and modes of scripts/install.sh (root, world-readable; the entries of lab/ and lib/ to the sre user, not world-readable), removes the stale __pycache__ directories, then runs make fonts sre-wrapper and makes sbin/sre and bin/sysreseval executable again.

What is installed, kept and deleted:

  • lab/: only lab/sre/ (params.update_lab_subdirs) is refreshed, and reinstalled in full; every other entry of lab/ is the site’s own and is left alone.

  • lib/: the files of the repository are refreshed; the site’s extra files are kept.

  • Files the repository dropped are deleted only when a previous update (or the git index of a cloned tree) installed them: the first update of a tree that was not cloned deletes nothing.

  • Never touched: venv/, build/, graphics/fonts/, bin/sre-wrapper, .git/, the params.py backups.

  • src/SRE/params.py is merged, not copied. The installed file is backed up as params.py.bak.<timestamp> first. A parameter the site never changed takes the new value; a parameter customised here that the new version does not change keeps its value; a parameter both customised here and changed upstream is asked about, the installed value being the default (kept with --yes). sre_version and default_docker_image_version always take the new value; new parameters are added, dropped ones removed. The pristine upstream values come from the state file (or git show on a cloned tree); without them, every difference is asked about. The merged file must compile and run before anything is replaced.

The state file .sre-update.json records the commit, the ref, the date, the previous commit, the manifest of the installed files and the pristine parameter values. Hints printed at the end: the command to go back (sre update --ref <previous commit>), make venv when the venv recipe of the Makefile changed, the scripts/etc/ templates that changed (system files to reinstall by hand), and the reminder that replicated hosts need their own sre update and NFS clients their own sre wipe.


Exam management

Exam state lives in /var/lib/sre/exam.json — see Exam Reference for the field schema. All field names are exposed as params.exam_* constants.

sre set-exam [options]

Creates or updates exam.json. Only provided options are updated; existing fields are preserved.

Option

Description

--labs <lab[:flavor]…>

Authorised labs, optionally with a named flavor preset (e.g. tp1:hard). Required on first creation.

--start-after <datetime>

Exam opens after this moment. ISO (2026-06-01T09:00) or time-only (09:00, today).

--end-before <datetime>

Exam closes before this moment.

--duration <minutes>

Max duration; ends at started_at + duration or end_before, whichever first. Effective only once formally started.

--eval-interval <seconds>

Period between automatic evals (default 60).

--record-sessions <bool>

Record terminal sessions (true/false/0/no).

sre del-exam

Removes exam.json and wipes all running projects.

sre save-records -d <dir> [--only-last-record]

Archives the records/ directories of running projects as .tar.gz files in <dir>. --only-last-record deletes prior archives for the same project so only the latest remains.


Post-exam management

Reads and transforms .zst evaluation archives — see Archive format for the schema.

sre cat [options] <file…>

Prints archive contents. Without a field option, all fields are shown.

Option

Shows

--data

data_json (serialized Data)

--tests

Raw test results per machine

--errors

Evaluation errors

--answers

Student answers

--grades

Grade list, total grade, total max

--files

Files saved in the archive

--extract-files

Extract saved files to the current directory

--json

Emit a single JSON dict per file on stdout

sre check-eval [-s <srelab>] <file…>

Diffs stored grades against a re-grade (same logic as re-eval but without writing files). Prints per-element changes plus a summary count. -s selects an updated srelab.py or directory; if omitted, uses each archive’s .private/srelab symlink.

sre re-eval -s <srelab> -p <prefix> [-d <outdir>] [-r] [--update-web] <file_or_dir…>

Re-grades archives with an updated srelab.py. Useful for fixing a grading bug post-exam.

Option

Description

-s / --srelab

Updated srelab.py or directory.

-p / --prefix

Prepended to each output archive filename.

-d / --output-dir

Output directory (default: current).

-r / --recursive

Recurse into subdirectories.

--update-web

Also replace the evaluations the web portal holds for these archives (see below).

--update-web is for a site with the web front-end: the Archives page of the portal shows what it collected when each evaluation ran, and a re-graded archive does not change it by itself. With the option, the new results of every archive re-graded are queued on the host, in /var/lib/sre/web-updates/, under the name of the archive they come from — the name the portal knows the evaluation by, whatever the prefix and the output directory — and the web agent of the host hands them to the portal at its next collection (30 s by default), which replaces the grades, marks, errors and elements of its row. See Re-evaluated archives for what the row keeps.

  • Run it as root on a pool host (a host with sre-agent): the queue belongs to the sre user, and nothing reads it on a host without agent (a warning says so). Any other user gets an error before anything is re-graded.

  • Give the archives under their original names (the files of /var/lib/sre/archives, or copies of them): the output of an earlier re-eval (corrected2026…) and a renamed archive are re-graded but not queued, with an error: line each.

  • The portal only updates evaluations it holds. An update whose evaluation it has not collected yet waits on the host and is applied once it has; it is given up after a day.

  • The command ends with the number of evaluations queued; the portal’s log says when it replaced them.

sudo sre re-eval -s /opt/sre/lab/_EXAM_/ssh_corrected.py -p corrected -d /tmp/regraded \
                 --update-web /var/lib/sre/archives/*ssh*

sre sheet -o <output.ods> [-r] [-S <time>] [-F <time>] [--separate-instances] <file_or_dir…>

Exports archives to a LibreOffice ODS spreadsheet. Per distinct lab name, three sheets are produced:

Sheet

Content

{lab_name}

One row per archive, sorted by login, hostname, project_start, eval_date: login, fullname, email, hostname, project_start (start of the running project instance), eval_date, errors, total_grade, total_max, mark, maximum_mark, then one column per grade element

Questions {lab_name}

Per-question: max_grade, maximum grade, average, number of projects, projects grade 0

Sessions {lab_name}

Per-student best score: login, hostname, max score, sum of maxima, then best score per question. With --separate-instances, one row per running project instance instead (a student who opened the lab twice gets two rows), with a project start column after hostname

fullname and email come from the archive’s answers (empty if absent).

sre outline [-o <summary.ods>] [-d <pdf_dir>] [-r] [-S <time>] [-F <time>] [--lang <lang>] [--no-timeline] [--remaining-time] [--separate-instances] [--users-file <file>] <file_or_dir…>

Generates per-student PDF reports plus a summary ODS — best-graded archive per student (login + hostname + lab). At least one of -o / -d is required.

When a student opened the same lab several times, the best grade is taken across every instance and the evaluation history shows one table per instance, each headed by its start time. --separate-instances produces one PDF and one ODS row per running project instance instead: the PDF name gains __<start timestamp>, its header a “Project started” line, and the ODS a project_start column.

The PDFs are named <login>__<lab>__<hostname>.pdf, <lab> being the file name of the lab. When two labs of one student would give the same name (a course copy and the shipped lab, s3/dns1.py and sre/dns1.py), the second one is named after its whole path (<login>__sre-dns1__<hostname>.pdf) instead of being written over the first.

The hostname is part of what a student is here (the workstation: the users of a shared account are told apart by it). The reports of the web front-end, built by sre report from the same code, leave it out: a project of the web runs on any host of the pool (see Web front-end).

Option

Description

-o / --output-file

Output .ods summary (omit to skip).

-d / --pdf-directory

Output dir for PDFs (omit to skip).

-r / --recursive

Recurse into subdirectories.

--lang <lang>

Force PDF language (e.g. en, fr).

--no-timeline

Omit the evaluation-history table.

--remaining-time

Include the time-remaining column in the history.

--separate-instances

One report and one ODS row per running project instance instead of one per student.

--users-file <file>

User list LOGIN NAME EMAIL (whitespace/CSV; # comments; <3-field lines skipped).

Name / email resolution: --users-file (if provided) → fullname / email from archive answers. Columns appear only when at least one student has a non-empty value.


Internal commands (GUI-only)

sre list [--with-titles] [--include-exam-only]

Prints a JSON array of lab names relative to /opt/sre/lab/ (s4/tp_ssh, s2/lab.py, …). Labs whose components match params.exam_only_affix (_EXAM_, _OLD_, _DRAFT_, _TESTS_) are hidden, unless --include-exam-only is given (privileged only: the web agent lists them to an instructor programming an exam; a student can still start such a lab by its exact name, which is how an exam project is created).

With --with-titles, output becomes the lab tree flattened in display order: [{"name": <path>, "title": <dict|string|null>, "kind": "dir"|"lab", "sort": <mode>}, …], a directory entry ("dir", listed when a lab lies below it) right before its content, the sub-directories of a directory before its labs (a directory lab is a "lab"), each group in the order of the effective "@sort" of the directory’s titles.json (see sre make-titles; sort is that mode, "name", "title" or "manual": the GUI and the browser keep this order and only sort the "title" mode themselves, since it depends on the language of the user). Titles come from per-directory titles.json; stale entries are dropped, and entries without a title get "title": null (the front-ends then fall back to the file name).

sre export <running_lab> [--sep <N>] [--curved] [--shapes] [--reverse] [--random-seed <N>]

Exports a running project as a Kathara zip archive, base64-encoded on stdout. The project name is resolved like the other commands (exact name or unique substring, the ownership check of user mode). Flags tune the embedded schema: --sep 0–9 (default 3), --curved for curved edges, --shapes for geometric nodes, --reverse to flip insertion order, --random-seed for node-order permutation. The lab.conf of the archive carries the type of the networks that are not hubs and the VLANs of the ports (CD_MODE[lan]="managed", pc1[0]="lan/vlan=10", r1[0]="lan/trunk=10,20"): this is the syntax of the Kathara fork SRE installs, not of the upstream Kathara.

sre report <sheet|outline|summary> [--lang <lang>] [--separate-instances] [--bonus-in-prefix] [--no-timeline] [--remaining-time] [--no-parts] [-o <file>]

What the web agent runs for the Reports of the Archives page of the portal: the spreadsheet of sre sheet (sheet: an ODS), the PDF reports of sre outline (outline: a zip holding one PDF per student, lab and host, and outline.ods, the summary) or that summary alone (summary: the ODS sre outline -o writes, one row per student, lab and host — per running instance with --separate-instances — with the best evaluation; the PDF flags --no-timeline, --remaining-time and --no-parts do not apply), built not from .zst files but from JSON archive records read on stdin ({"records": [...]}: the records of GET /v1/archives of the agent, what the portal keeps of every archive in its database and rebuilds for the evaluations an instructor selected), and written on stdout (-o <file>: a file instead). Each record becomes the pseudo archive the two commands read (archive_summary.raw_from_record: the student metadata, the time, the totals, the grade elements and parts, the number of errors; the tests and the answers were never in a record), so the files are those sre sheet / sre outline give on the same archives. The flags are those of the two commands (--lang applies to the PDFs; the headers of the spreadsheets follow the LANGUAGE of the environment). Privileged only (an admin may run it like sheet and outline); exit 1 with no records to process on an empty or unusable list, a record without running_lab_name is skipped with a warning on stderr.

sre pre-start-exam

Stops non-exam projects, then starts all exam labs so images are pre-pulled. Called by the GUI ~60 s before start_after. In user mode with params.check_project_owner on, the projects of the other users of the host are stopped too, whatever their lab (the GUI only counts the caller’s own tabs), and an exam lab is started unless the caller, or root, already runs one.

sre start-exam

Stamps started_at in exam.json. Called by the GUI once now ≥ start_after.

sre eval-exam

Evaluates every running exam project concurrently. Called by the GUI on a periodic timer (every eval_interval) and once at exam end.

sre end-exam

Stamps ended_at and runs a final concurrent eval on every running exam project. Called by the GUI when the exam ends.