Demo mode
Demo mode turns a tenant into a public demo that anyone can try without an account. Visitors of the learner site are logged in as the demo student, visitors of the admin panel as the demo admin, and the whole tenant is wiped and seeded again every hour.
The six demo tenants of the reference setup, coffee, oncall, nightsky, gravity, poland and ulam, run in demo
mode (see Demos).
Turn it on or off
Section titled “Turn it on or off”Demo mode is on when the tenant’s env file .env.<host> contains DEMO_MODE=true. The default
is off. Set it through the tenant registry, so that ulams:tenant:sync-env keeps the value:
php artisan ulams:tenant:create coffee --demo=on # or --demo=offOnly the env file is rewritten; no data changes. To turn it on for the six demo tenants at
once, run from api/:
make demo-mode-onThis runs ulams:tenant:create <slug> --demo=on for all six.
ulams:tenant:list shows the current state in the demo column.
When demo mode is off, none of it exists: the demo routes answer 404, GET /api/config has no
ulams_demo key and no reset is scheduled.
Password-less login
Section titled “Password-less login”| Endpoint | What it does |
|---|---|
GET /api/demo |
Returns enabled, users (the demo accounts: role, email, never a password; the tutor only when that account exists), front_url, admin_url and reset_cron (null when DEMO_RESET_SCHEDULE is off). |
POST /api/demo/login with {"role": "student"}, {"role": "tutor"} or {"role": "admin"} |
Issues a regular Passport personal access token for that account. The response body is the same as POST /api/auth/login. Rate limited to 60 requests a minute. A role whose account cannot be found (for example a tenant without a seeded tutor) answers 422. Any other role value is rejected by validation. |
GET /api/config |
Gains ulams_demo: {enabled, front_url, admin_url}. |
Before logging in the student, the API gives the demo student access to every published course (through the course access service), so any course link opens with access.
The admin panel and the legacy React learner app read ulams_demo from GET /api/config at
start-up. When nobody is logged in, the admin panel logs in as the demo admin and the learner
app as the demo student, and each shows a demo badge that links to the other application.
The reference front (front/web) calls POST /api/demo/login with the student role on the
server and shares one cached demo token per tenant between visitors. If the tenant has no demo
mode (404), it falls back to a password login only when a demo student password is configured
for the front.
The accounts used:
| Role | Account | Override |
|---|---|---|
| admin | INITIAL_USER_EMAIL, by default admin@<slug>.ulams.app |
DEMO_ADMIN_EMAIL |
| student | student1@ at the admin’s e-mail domain |
DEMO_STUDENT_EMAIL |
| tutor | tutor@ at the admin’s e-mail domain (the course author in the studio) |
DEMO_TUTOR_EMAIL |
If the account does not exist, the first user with that role is used.
Hourly reset
Section titled “Hourly reset”The package schedules ulams:demo:reset --force --domain=<host> on every domain whose env has
DEMO_MODE=true, by default at the start of every hour (DEMO_RESET_CRON, 0 * * * *). The
container scheduler already runs schedule:run for every tenant, so nothing else needs to be
set up. The reset runs in the background and never overlaps with itself (withoutOverlapping, with a 120-minute
lock in the cache store).
To reset a tenant now:
php artisan ulams:demo:reset --force --domain=coffee.localhostmake demo-reset DOMAIN=coffee.localhost| Option | Meaning |
|---|---|
--force |
Do not ask for confirmation. |
--wipe-files |
Also delete every file in the tenant bucket (same as DEMO_RESET_WIPE_FILES=true). By default uploads stay, and the demo assets are uploaded again at the same paths. |
--recapture |
Take a new baseline (name, theme, accent, students) from the current database before wiping. |
The command refuses to run when demo mode is off on that domain, and always refuses on the platform.
What the reset does
Section titled “What the reset does”- H5P content. Deletes the tenant’s H5P contents in the H5P service (
DELETE /h5p/contents/{id}for every content id the tenant database knows, fromh5p.contentsand from H5P topics), then the files of contents that no longer have a row (POST /h5p/contents/orphans/delete). The service stores H5P content outside the main tenant schema, somigrate:freshalone would leave it behind. If the service cannot be reached, the reset continues with a warning and the next reset deletes them. - Database.
migrate:fresh --forceon the whole tenant database, including the OAuth tables. Every token issued so far stops working; the admin panel and the front log in again by themselves. - Passport client. Creates the personal access client again.
- Roles and admin. Seeds roles, permissions and the admin account.
- LTI keys.
ulams:lti:rotate-keys --initcreates the tenant’s LTI key set again (it lives in the wiped database). Skipped when the LTI package is not installed. - Users and settings. Creates the tutor and the students and stores the name, theme and accent from the baseline.
- Demo content.
ulams:demo:seed: seeds the demo courses for the tenant’s experience (by default the tenant slug) and gives the demo student access to every published course.
The tenant’s Redis cache keys are deleted after the database wipe and again at the end. The
shared Redis cache is not flushed, so other tenants are not affected. Each step runs as a
child php artisan … --domain=<host> process.
Baseline. Name, theme, accent, front URL, number of students and e-mail domain are stored
in the database being wiped. The first reset saves them to
storage/<host>/app/ulams-demo-baseline.json (for example
storage/coffee_localhost/app/ulams-demo-baseline.json), and later resets reuse that file. A
theme change made in the demo admin therefore does not survive the next reset. Use
--recapture, or change the tenant with ulams:tenant:create, to keep a change.
Seed without a reset
Section titled “Seed without a reset”php artisan ulams:demo:seed --domain=coffee.localhostphp artisan ulams:demo:seed --skip-content --domain=coffee.localhost--experience= picks the demo content (coffee, oncall, nightsky, gravity, poland, ulam or all; default
ULAMS_DEMO_EXPERIENCE, then the tenant slug). --skip-content only grants the demo student
access to the published courses, for example after courses were created by hand.
The gravity, poland and ulam packages
Section titled “The gravity, poland and ulam packages”The gravity, poland and ulam experiences play interactive packages from demo-content/, which the API
container cannot see (it mounts only api/). make -C api demo-packages builds them on the host
(Chromium is needed for the gravity posters) and puts gravity.zip, poland.zip and the five ulam-*.zip
(ulam-spiral, ulam-monte-carlo, ulam-automaton, ulam-scottish-book, ulam-lwow-map) in
api/database/seeds/Demo/assets/cache/interactive/, where the seeder reads them. A missing zip stops the
seeding with a message that names it, instead of leaving the courses without their lessons. On a server, build
the zips once and copy them to that folder. The ulam course also reads four photographs from
api/database/seeds/Demo/assets/ulam/images/ (committed, so nothing is fetched at seed time). Re-seeding with the same zip reuses the package; a changed
zip becomes a new version of it.
Configuration
Section titled “Configuration”Set these in the tenant env file. Defaults are in
api/packages/demo/src/config.php.
| Variable | Default | Purpose |
|---|---|---|
DEMO_MODE |
false |
Turns demo mode on. Set through ulams:tenant:create --demo=on. |
DEMO_ADMIN_EMAIL |
INITIAL_USER_EMAIL |
Account for the admin login. |
DEMO_STUDENT_EMAIL |
student1@<admin domain> |
Account for the student login. |
DEMO_TUTOR_EMAIL |
tutor@<admin domain> |
Account for the tutor login. |
DEMO_ADMIN_URL |
ADMIN_URL |
Admin URL shown on the front’s demo badge. |
DEMO_RESET_SCHEDULE |
true |
Schedule the reset. |
DEMO_RESET_CRON |
0 * * * * |
When the reset runs. |
DEMO_RESET_WIPE_FILES |
false |
Also empty the bucket on every reset. |
DEMO_RESET_STUDENTS |
5 |
Number of students when no baseline exists yet. |
DEMO_CONTENT_SEEDER |
Database\Seeders\DemoCoursesSeeder |
Seeder for the demo courses; skipped if the class does not exist. |
ULAMS_DEMO_EXPERIENCE |
tenant slug | Which demo content to seed. |
See also Environment variables, Artisan commands and Scheduled jobs.
Known limitations
Section titled “Known limitations”- Everyone shares the same demo student and demo admin, so visitors see each other’s changes until the next reset.
- Files uploaded to the bucket are kept across resets unless
--wipe-filesis used. - More detail on the reset internals is in the demo package README.