ISC Learn — history & operations
Dated operations on ISC Learn and its server hannibal, newest first. Current state is on the ISC Learn and hannibal pages; the marcellus log is on External VPS.
2026-09-09 — frontpage: ambient background, spacing, menu
Ambient background ported from the hub landing page (the layer under <body> needs the theme's
white #region-main and .pagelayout-content made transparent), hero frame and graph paper a shade
darker, course and « Mes cours » blocks shown at 0.7 s instead of 1.3 s (per-image fade instead of
waiting for every image),
petal mark moved to the bottom-right corner, one 44 px gap between all blocks, typewriter no longer
wiping its first word, the edu-ID button opening the header's login window, « ISC Hub » as a root
menu entry and « En savoir plus » pointing at the landing page. Six deploys of
frontpage-2a/deploy.sh from the Mac (now portable) and one menu script; standing description on
the frontpage section.
2026-09-09 — theme AJAX blocks requested at HTML parse time
The frontpage's semester tabs, blog row and « Mes cours », and the course panel of the toggle
sidebar, were empty containers New Learning filled by AJAX once first.js had run — 0.6 s after
the HTML here, after load on a throttled mobile. Two inline scripts now send the same web-service
requests at parse time and inject the same markup: loader.html on the builder page (the theme's
frontpage loaders are neutralised and tab clicks handled by the script) and a block of
header.html for course pages (the theme's call still follows and re-renders the same panel).
Frontpage blocks at 0.5 s instead of 0.7 s on a fast connection, 4.5 s instead of 32 s under
PageSpeed mobile throttling; course panel 0.8 s instead of 1.3 s, 4.9 s instead of 32 s; the course header image, lazy-loaded
by the theme at ~2 s, set at 0.8 s the same way. The
2026-09-05 theme-file patch rendering the course panel inline was retired the same day
(provisioning/learn/mb2nl-tgsdb-chome-restore.sh, patched file kept beside the original as
lib_toggle_sidebar.php.isc-patched-2026-09-09); the deploy script and apply-frontpage-2a.php
carry the scripts, deploy.sh now also runs from macOS. Standing description and what can break
it: early fetch; numbers: performance.
2026-09-09 — 2026-27 students registered
First run of the semester scripts from ISC-HEI/moodle-isc-admin-scripts
(the yearly-archive repository, renamed): 33 accounts created (auth=shibboleth, no forced
password change) and 371 per-module enrolments in the 15 autumn courses, from the secretariat's
workbook. Per-student enrolments replace the year cohorts. Left for a person: the bachelor thesis
330.1 (labelled S6 in the workbook), three X? marks, four modules without a course
(todo). Procedure: registering students.
2026-09-09 — file-system repository aliases moved to personal instances
The 2026-27 courses 101.1, 101.2 and 205.1 had their 350 "File system" aliases spread over nine
repository instances belonging to archived courses (two of them still serving the 2022 folder
mui); they now go through two instances in pmudry's user context (101, 205.1), which course
copies inherit and which no course deletion can convert to frozen copies. Standing rule on the
service page.
2026-09-08 → 09 — second performance pass: Apache, PHP-FPM, content, theme
Server side, in sites-available/isc.hevs.ch.conf — the standing configuration is on the
hannibal page:
- SVG added to the vhost's own brotli list (the ISC logo 12 446 → 2 926 bytes, 53.8 kB across the five site SVG).
mod_expiresblock removed (it capped HTML at 5 days, so returning visitors saw a stale site);Headerrules instead, andpluginfile.phpresponses rewritten tomax-age=3600.conf-enabled/isc-hub.confdeleted — it held those rules at server scope, on all seven vhosts.- PHP-FPM pool: start/min/max spare 2/1/3 → 4/3/8,
max_children20 unchanged; slowlog at 3 s. - X-Sendfile enabled: 78 ms before, 77 ms after on a 110 kB file — kept for the freed worker and byte ranges, not for speed.
learn.isc-vs.chgot its own vhost (301 toisc.hevs.ch/learn/); it had none and fell through tofiles.isc-vs.ch, answering "Nothing to see here" under a certificate that did not cover it.
Moodle side:
- Two frontpage blog images 5776×3848 (3.3 MB) and 4000×2664 (2.7 MB) → 1600 px WebP at 265 and
182 kB; page weight 8 507 → 2 774 kB. An LTI tool icon pointing at
challengeme.online(a 4664×4664 PNG for a small logo, third-party fetch) removed. - Theme patch: the toggle sidebar's course header rendered inline instead of by AJAX ~2.1 s later
(a theme-file patch, lost on every update; retired 2026-09-09 for the
early fetch). Trap kept from it: never call
theme_mb2nl_external::course_chome()from a page render, itsvalidate_context()resets the page output. - Toggle sidebar drawn right at first paint (2026-09-09): the unitless
--tgsdb_cheight: 0left the column at content height (670 px in a 900 px viewport) untiltgsdb.jsran; fixed in CSS alone, appended to the Custom CSS setting withprovisioning/learn/mb2nl-tgsdb-css.sh, measured identical before and after the JS runs. - Frontpage typewriter script repaired (2026-09-09): a hand save in the builder editor on
2026-09-07 had replaced every
[…]of the hero script by a "no shortcodes" notice, a JS syntax error on every load. Repaired in place withprovisioning/learn/frontpage-hero-js-2026-09-09.php, other hand edits kept; the script is now bracket-free. $CFG->enable_read_only_sessionstried twice and reverted (Moodle refuses to start while any session-mode MUC definition resolves to the session store).
Result: PageSpeed mobile 46 → 53, LCP 20.2 → 13.0 s; course page 361 ms median server render;
isc.hevs.ch/landing/ 81 → 99. What remains and what was ruled out:
performance.
2026-09-07 — hannibal: accounts pruned, sshd and Docker hardened, 308 updates
Triggered by the marcellus compromise (same
admins, same history). Audit first (read-only), then three scripts in provisioning/hannibal/:
accounts-2026-09-07.sh— deletedpim(sudo,sudonopass),leny.bressoud(docker) andremi(sudo, no key since 2024) with their homes; pim's three keys removed fromubuntu,marks_devandmarks_prod;yacine.saidadded as admin. The Snipe-IT compose project lived in leny's home: moved to/srv/docker/inventory(same project name, volumes untouched), ports bound to 127.0.0.1, container recreated — a few seconds of downtime. Everything removed is in/srv/removed-accounts-2026-09-07/. pim's home server had been pulling a full rsync of/and/srvnightly at 01:00 asubuntu; that copy stops with his key.harden.sh—ProxyRequests Offinmarks,marks.devandinventory; sshd drop-in (no root, no TCP/agent/X11 forwarding, 3 attempts, idle timeout);DOCKER-USERdrop of 8080/8443/8501/8502 onens3persisted byisc-docker-ports.service;unattended-upgradesreinstalled (it had been removed), security origin only, no automatic reboot.system-update.sh— 308 packages. The kernel reboot is pending (todo).
Verified from outside: only 443 and 20002 answer, CONNECT through the reverse-proxy vhosts is
refused, Snipe-IT, marks and Learn answer 200. Break-glass checked: the ubuntu console password
in secretzone/hannibal.md matches the shadow hash, both getties (tty1, ttyS0) are active.
Afternoon, after a read-only check for the marcellus pattern (no leftover installer, dump or
dropped file): the ingegamez WordPress got auto-updates for every plugin and theme and its seven
pending plugin updates were applied through the WordPress updater API (no wp-cli on the host; a
first attempt left the auto_updater.lock option behind, which the second run had to delete); the
Moodle code tree lost its 2 076 group-writable and 1 257 world-writable paths, all in the
web-installed New Learning plugins. Learn and ingegamez answered 200 throughout.
2026-09-06 — six plugin updates
Moodle's plugin manager had been reporting updates since the 5.2 upgrade: FilterCodes 2.7.2 → 3.0.1,
Components for Learning (tiny_c4l) 3.1.0 → 3.3.0, Redis tool, Bulk enrol and Static page v5.0 →
v5.2-r1, Mailtest 3.1.7 → 3.1.8. Run on the VM 107 mirror at 16:16, then on hannibal at 18:23 with
provisioning/learn/plugin-updates.sh hannibal: btrfs snapshot /srv/.snapshots/pre-plugins-2026-09-06,
zips from moodle.org checked against the published md5, previous plugin directories kept in
/srv/plugin-updates/2026-09-06/prev/, upgrade.php in 39 s with no maintenance mode (four new
FilterCodes settings at their defaults). Verified: pages 200, theme CSS unchanged, cron running, no
PHP error. Log: hannibal:/srv/plugin-updates/2026-09-06/run-1823.log.
2026-09-06 — new frontpage design
Design « 2a » from a Claude Design hand-off, reworked on the VM 107 mirror during the afternoon
(PVE snapshot pre-frontpage-2a taken first) and applied to hannibal at 16:40 on a Sunday: no
downtime, one cache purge. The hand-off was a Boost child theme; New Learning reads its settings
from the active theme, so a child theme loses them all — the design went into the theme's Custom
CSS and a builder page instead, which also survives theme updates. Kept from the old page: the
semester course tabs with their images, the blog row and the footer; removed: the resources
carousel, the « philosophie » text and the typewriter line, which now lives in the hero. Standing
description and traps: frontpage.
2026-09-06 — content touch-ups and course images
All rehearsed on VM 107, applied on hannibal with scripts in provisioning/learn/, each with
--revert:
- Teaching-team page (Mermoud professeur ordinaire, Zahno on processor architecture, Andrea
Guerrieri added for Digital design, Xavier Barmaz removed) and the « En savoir plus » menu item
pointing at the hub's prospective-students page —
team-menu-2026-09-06.php. - Navigation root reads « Infos » (was « Infos... »), its dropdown opens with an « ISC Hub » link —
menu-infos-2026-09-06.php. - Phone pass: semester tabs as a 3 × 2 grid of pills, the notification bell hidden below 820 px
with a « Notifications » entry added to the user menu, the footer button « Plus d'infos sur la
formation » pointing at the hub —
footer-usermenu-2026-09-06.phpplus the frontpage-2a CSS redeployed (course-code line on every course listing, header logo proportions on phones). - Course images for 200.3, 304.2 and 305.2: three flat SVG illustrations in the style of the
Digital design card (ISC palette, no third-party photo), set as course overview files with
course-images.php; 304.2 had been showing the Robot control picture.
2026-09-06 — mirror refreshed
VM 107 on the 06:25 dump and the day's files, PVE snapshot pre-refresh-2026-09-06 taken first,
frontpage and content scripts re-applied. Two fixes to provisioning/drp/learn-mirror-refresh.sh:
moodle_data/trashdir is excluded (89 GB of purged course backups were being copied), and the
script must run from a file on rumba, not bash -s, since its inner ssh calls swallow stdin
(runbook).
2026-09-05 — first performance pass
Read-only survey first: TTFB 150 ms, HTTP/2, brotli, 90-day immutable asset caching, PHP-FPM never
at its 20-worker cap in 7 days. Four server-side leftovers, all changed online with no downtime and
one user connected: InnoDB buffer pool 128 MB for a 567 MB database (resized to 2 GB, persisted in
mysqld.cnf); OPcache at PHP defaults (128 MB, 10 000 files, revalidate 2 s) for a 51 000-file
tree (256 MB / 30 000 / 60 s, PHP-FPM reload); logstore_standard never purged (1.87 M rows,
372 MB of the 567 — retention set to 365 days, the rows before 2025-09-06 deleted through Moodle's
own cleanup task, 708 680 rows left; safety net: the same-evening pre-upgrade dump); and Redis 7.0
installed but unused — sessions and the MUC application cache moved to it
(provisioning/learn/learn-redis-enable.sh, rehearsed on the mirror an hour earlier with a real
login). Pages 200 in 0.11–0.16 s before and after: on a single request these settings change
nothing measurable, they matter on heavy pages and under load. Standing settings:
service page; scripts provisioning/learn/learn-perf-2026-09-05.sh and
learn-redis-enable.sh, log db_backups/manual/perf-2026-09-05-2346.log.
2026-09-05 — Moodle 5.0.1 → 5.2.2+
Done at 23:15 on a Saturday, 118 s of maintenance page, no user connected. Moodle 5.0 security
support ends 2026-10-05; 5.3 LTS is released that day and is the next step, at a holiday.
Rehearsed the same afternoon on the mirror (VM 107), which is now also on
5.2.2+; the rehearsal caught two things prod then avoided — Moodle 5.2 refuses MySQL 8.0 (the
mirror had Ubuntu's, it now runs Oracle's 8.4 like hannibal), and a theme archive unpacked on a
Mac ships ._* AppleDouble files into the tree.
Sequence (provisioning/learn/moodle-upgrade-cutover-2026-09-05.sh): read-only btrfs snapshot
/srv/.snapshots/pre-upgrade-52-2026-09-05 and a --single-transaction dump
(db_backups/manual/moodle_isc_pre-upgrade-52_2026-09-05.sql.gz); new tree built beside the live
one from the archive tested on the mirror (Moodle 5.2.2+ build 20260903 plus the 23 non-standard
plugins: New Learning 12.2.7 and companions from the vendor package, VPL 4.5.0, CodeRunner 5.10.1
and behaviour 1.4.7 from GitHub, the rest carried over) with prod's config.php; the three
moodle_isc paths in isc.hevs.ch.conf pointed at moodle_isc/public (Alias, <Directory>,
Shibboleth block; previous file kept as .pre52); CLI maintenance, directory swap,
admin/cli/upgrade.php --non-interactive in 116 s (29 new settings at their defaults), cache
purge, PHP-FPM restart. Verified: pages 200, Shibboleth entry redirects to the SWITCH WAYF, theme
width 1600 px, cron running, no PHP or Apache error. Old code kept at moodle_isc.50 for a week
(todo); the year-move-up scripts that sat in the old web root went with
it, so they are no longer reachable over HTTP.
Two findings kept on the service page: the public/ layout, and the New Learning
12.2.7 page-width bug with its one-line patch. Left open: Moodle's router check. Log:
hannibal:/srv/upgrade-52/upgrade-2026-09-05-2315.log.
2026-09-05 — course backups purged
moodle_data was 303 GB logical, of which 280 GB were automated course backups: 5 versions of
every course, stored twice (directory + course file area, block-shared on btrfs), and never pruned
for the 116 archived courses because Moodle only prunes what it backs up. Applied rule: 3 versions
for the 25-26 courses and for active ones, 1 for 22-23 to 24-25. Removed 410 file-area entries
through the File API and 390 directory .mbz from pre-generated lists, checked file by file
against the rumba copy first; 195 / 199 kept, backup_auto_max_kept 5 → 3. Lists and logs:
hannibal:/srv/www/learn.isc-vs.ch/db_backups/manual/purge-2026-09-05/. About 92 GB physical,
released once Moodle's trash task runs and the read-only snapshot
/srv/.snapshots/pre-purge-2026-09-05 is deleted (todo). Same day, the
end dates of 25-26 / 330 - TB and 25-26 / 105.1 - SS were set to 2026-09-04 23:00
(update_course(), which requires startdate in the object). Also the same day the frozen
Aug-2024 Moodle on marcellus was deleted after checking every course exists here under Archives
(marcellus log).
2026-09-05 — mirror refreshed, rumba copy versioned
VM 107 now carries the 2026-09-04 dump and moodle_data as of the day; five weeks of drift were a
37 GB delta pull through the tunnel, about an hour. The steps became
provisioning/drp/learn-mirror-refresh.sh and a runbook section.
The rumba copy moved into its own dataset hdd/hannibal-mirror (block-cloned, 4 min, no extra
space; why not hdd/backup); the first snapshot
hannibal-2026-09-05 holds the pre-purge course backups, and the script snapshots after every pull
with an 8-weeks-then-monthly retention. The pull now also carries the rest of the host
(/etc/shibboleth, /etc/letsencrypt, fail2ban, crontabs, root's keys, the other vhost roots,
the docker volumes — under 2 GB).
2026-08-22 — isc.hevs.ch serves the hub instead of redirecting to it
The address bar keeps the institutional name. DocumentRoot /srv/www/isc.hevs.ch/hub, fed from
the isc-hub repo's deployed branch by a 5-minute pull timer, read with a read-only deploy key
(provisioning/web/hannibal-hub/install.sh); a build propagates within one tick. The hub dropped
the # from its URLs the same day, so the block carries FallbackResource /index.html; /learn
is unaffected. The fleet links moved to isc.hevs.ch, making it the canonical of the two names.
Standing state: hannibal; the trade against host-independence:
migration plan.
2026-08-21 — the root of isc.hevs.ch 301s to the hub
Execution of the 2026-08-11 decision, four items:
- DNS:
hub→ CNAMEisc-hei.github.io.in our Infomaniak zone. - The hub repo: base
'/isc-hub/'→'/',public/CNAME=hub.isc-vs.ch, the absoluteog:/twitter:URLs.lighthouserc.jsonwas the tripwire — it auditedhttp://localhost:4173/isc-hub/and its workflow gates the deploy, so at base/nothing published until it was changed. Pages is aworkflowdeployment, so the domain is set withgh api -X PUT repos/ISC-HEI/isc-hub/pages -f cname=hub.isc-vs.ch(thatPUTclearshttps_enforced; set it back with a secondPUT … -F https_enforced=trueonce the certificate is approved), plusgh api -X PATCH repos/ISC-HEI/isc-hub -f homepage=https://hub.isc-vs.ch/, sincepublic/repos.jsonis regenerated daily from that field. - The
isc-hei.github.io/isc-hub/URL retired in 15 links across 7 repos (isc-hub,isc-tbs,isc-calendar,isc-curriculum,isc-typst-tb-descriptions,isc-marks-mail,isc-shirts, and this site's navbar and index). GitHub's automatic redirect keeps old links working and is the one part that dies if the hub ever leaves GitHub. Repointed again tohttps://isc.hevs.ch/the next day. - Hannibal:
RedirectMatch permanent ^/$ https://hub.isc-vs.ch/in the*:443block — replaced by theDocumentRootthe next day. The certificate needed nothing:isc.hevs.chstays a SAN on the existing lineage.
2026-08-10 — the shared certificate re-issued before it failed
wiki.isc-vs.ch had been retired to a redirect on srv-web01 while still a SAN on the
isc.hevs.ch lineage; the renewal due around 2026-09-21 would have failed all ten names silently.
Re-issued without it (ten names, all verified from outside), and the missing deploy hook that
reloads Apache added. Standing rule and command: hannibal. Marcellus
had the same problem, fixed the same evening (marcellus log).
The DokuWiki itself was archived off hannibal the same day
(where it is, the redirect vhost).
2026-08-07 — fail2ban fixed, facts refreshed, migration plan written
The sshd jail rejected on port 2002 while sshd listens on 20002, so ~20 000 failed attempts in
30 days had produced 14 bans that blocked nothing. Fixed and verified on IPv4 and IPv6; ubuntu
given a console break-glass password since SSH is key-only. Half of audit finding F-OPS-3 — the
missing host firewall stays open (todo). Same day, prod facts refreshed
read-only (Moodle 5.0.1 then, moodledata 303 GB of which 134 GB course backups, VPL/Jobe on
Infomaniak Jelastic, both public names CNAMEs to hannibal.isc-vs.ch — the older
"isc.hevs.ch → learn.isc-vs.ch" description was wrong) and the
migration to managed hosting proposed.
2026-08-01 — disaster-recovery mirror built on rumba
Full restore hannibal → VM 107 srv-learn-test, ≈ 2 h 40 min wall clock, dominated by the 293 GB
moodle_data transfer through a laptop tunnel at 20–25 MB/s (the rack cannot dial port 20002, and
learn.isc-vs.ch publishes AAAA the rack cannot route). Verified down to byte-exact
pluginfile.php downloads. The steps and the traps became the
from-scratch runbook; the staging copy stayed on rumba as the first on-site
backup of ISC Learn. Two findings went to the ops todo: the DS923 had
been the only copy, and the rumba_backup share on the FS2500 was an empty stub.
2025-04 — Moodle 4.4.2 → 4.5.4
Historical notes from the April 2025 upgrade of production, kept verbatim: the block of cp -a
lines was the de-facto inventory of the third-party plugins until the
5.2 upgrade rebuilt the tree from packages.
### New Migration from 4.4.2 to 4.5.4
# 9H00 : Maintenance mode
cd /srv/www/learn.isc-vs.ch/moodle_isc/
sudo -u www-data /usr/bin/php admin/cli/maintenance.php --enable
# Retrieve package : DONE
#cd /srv/migration_moodle
#wget https://download.moodle.org/download.php/direct/stable405/moodle-4.5.4.tgz
#tar -xzf moodle-4.5.4.tgz
#mv /srv/migration_moodle/moodle /srv/migration_moodle/moodle_isc
cd /srv/migration_moodle
# Backup moodle Data
cp -a /srv/www/learn.isc-vs.ch/moodle_data /srv/www/learn.isc-vs.ch/moodle_data_4.4.2
# Backup moodle config
cp -a /srv/www/learn.isc-vs.ch/moodle_isc /srv/migration_moodle/moodle_isc_4.4.2
# 9H10 : Backup DB
mysqldump -u root -p -C -Q -e --create-options moodle_isc > /srv/migration_moodle/moodle_backup_$(date +%Y-%m-%d).sql
#### Start moving files : Put aside the old moodle_isc and replace with the 4.5.4 moodle_isc directory
mv /srv/www/learn.isc-vs.ch/moodle_isc /srv/www/learn.isc-vs.ch/moodle_isc_4.4.2
cp -a /srv/migration_moodle/moodle_isc /srv/www/learn.isc-vs.ch/
# To ease reading for the copies
export migration="/srv/www/learn.isc-vs.ch/moodle_isc_4.4.2"
export target="/srv/www/learn.isc-vs.ch/moodle_isc"
cp -a $migration/config.php $target/
cp -a $migration/theme/mb2nl $target/theme/
cp -a $migration/mod/vpl $target/mod/
chown -R moodle:www-data $target
### The upgrade part :
cd $target
sudo -u www-data /usr/bin/php8.3 admin/cli/upgrade.php
# All plugins copies
cp -a $migration/local/mb2builder $target/local/
cp -a $migration/local/mb2slides $target/local/
cp -a $migration/local/mb2coursenotes $target/local/
cp -a $migration/local/mb2megamenu $target/local/
cp -a $migration/local/mb2notices $target/local/
cp -a $migration/local/mb2reviews $target/local/
cp -a $migration/local/staticpage $target/local/
cp -a $migration/lib/editor/atto/plugins/mb2shortcodes $target/lib/editor/atto/plugins/
cp -a $migration/lib/editor/atto/plugins/c4l $target/lib/editor/atto/plugins/
cp -a $migration/lib/editor/atto/plugins/code $target/lib/editor/atto/plugins/
cp -a $migration/lib/editor/atto/plugins/templates4u $target/lib/editor/atto/plugins/
cp -a $migration/filter/mb2shortcodes $target/filter/
cp -a $migration/filter/synhi $target/filter/
cp -a $migration/filter/syntaxhighlighter $target/filter/
cp -a $migration/filter/filtercodes $target/filter/
cp -a $migration/report/coursesize $target/report/
cp -a $migration/blocks/mb2azcourses $target/blocks/
cp -a $migration/question/behaviour/adaptive_adapted_for_coderunner $target/question/behaviour/
cp -a $migration/question/type/coderunner $target/question/type/
cp -a $migration/admin/tool/redis $target/admin/tool/
cp -a $migration/local/mailtest $target/local/
systemctl restart php8.3-fpm.service && systemctl restart apache2
cd /srv/www/learn.isc-vs.ch/moodle_isc/
sudo -u www-data /usr/bin/php admin/cli/maintenance.php --disable
# Erreur "cache definition does not exist local_mb2builder/pagedata" → vider le cache :
cd /srv/www/learn.isc-vs.ch/moodle_data/
rm -rf ./cache/ ./temp/ ./trashdir/ ./sessions/ ./localcache/ ./muc/
# Finir l'upgrade dans le GUI sans le plugin "stockphotos", puis le désinstaller
mkdir /srv/migration_moodle/quarantine_faulty_plugin
mv /srv/www/learn.isc-vs.ch/moodle_isc_4.4.2/repository/stockphotos /srv/migration_moodle/quarantine_faulty_plugin/
# Test Switch-Edu ID : connexion OK
############# Upgrade terminée #######