Upgrading to Showcase 26.4.1
This guide covers upgrading from Showcase 1.14.x to 26.4.1 and configuring the new capabilities introduced in this release: per-user authentication, RBAC, SSO, TOTP, analytics, and the new server architecture.
For a summary of what changed, see the 26.4.1 release notes.
What changed
26.4.1 is a substantial release. The presentation functionality and content model from 1.14.x carry over unchanged, but the implementation underneath has been replaced. From an operator's perspective, the differences that matter on upgrade are:
- New server. TypeScript/Node.js replaces Ruby. If you have custom scripts
or monitoring that targeted the Ruby process, retarget them to
showcase-server. - No more PostgreSQL. SQLite runs in-process. There is no separate database service to install, configure, or back up. Your app content (structures, media, schedules) migrates automatically on first boot.
- Per-user authentication. The shared-password Basic Auth from 1.14.x is replaced by per-user accounts with five roles (admin, author, presenter, front_desk, viewer). The first operator to visit the editor completes a setup wizard to create the initial admin account.
- Default HTTP port changed from 80 to 8080. Update firewall rules and
any bookmarks. Override with
SHOWCASE_EDITOR_PORTor--portif you need the old port. - Calendar versioning. Versions now follow YY.M.PATCH (26.4.1 = April 2026, patch 1). The number changed; the core presentation functionality did not.
Upgrade procedure
Step 1: Rebuild custom plugins (if any)
The Package::canBeUsed() function signature changed. Custom plugins compiled
against 1.14.x will not load in 26.4.1 — the runtime will reject them at
startup.
- If you have no custom plugins, skip this step.
- If you have custom plugins, rebuild them against the 26.4.1 SDK before installing the new server. No source-code changes are required; recompilation is sufficient.
- If you use pre-compiled plugins from a third party, contact the supplier for a 26.4.1-compatible build.
Standard plugins shipped with Showcase are already built for 26.4.1 and require no action.
Step 2: Verify maintenance entitlement
Confirm your maintenance entitlement covers the April 2026 release. Without it, the installer will refuse to activate.
Step 3: Install 26.4.1
- Download the 26.4.1 installer (.deb for Linux, .exe for Windows) from the Showcase downloads page.
- Run the installer on each Showcase node.
- Start the server. Confirm the startup log line:
[startup] HTTP server listening on 0.0.0.0:8080
Step 4: Complete the setup wizard
- Open the editor URL in a browser (
http://<server>:8080). You will land on/setup— the first-boot setup wizard. - Complete the form:
- Email address
- Display name
- Password (minimum 12 characters; at least one letter and one digit)
- The server creates a
role=admin status=activeaccount and redirects to/login. - Log in with the credentials you just set.
After the first admin exists, the setup wizard is permanently disabled.
Subsequent visits to the editor URL go straight to /login.
Step 5: Invite the rest of your team
In the editor, go to Admin → Users → Invite and send invite emails to each team member. Assign roles per the User roles reference below.
If SMTP is not yet configured, you can create user accounts non-interactively
via the CLI — see users:create-admin below.
Step 6: Verify
- Open each of your apps in the editor; confirm they load.
- Launch one app on a player; confirm everything renders correctly.
- Check that custom plugins (if any) loaded — the server log reports any rejected packages at startup.
Your content is preserved
Structures, themes, schedules, media, and configuration carry over unchanged. Only the authentication subsystem is new and requires one-time setup.
Fresh install
For a brand-new deployment with no prior Showcase data, follow the Upgrade procedure starting from Step 3 — there are no plugins to rebuild and no content to verify.
User roles
Every user account has exactly one role.
| Role | Editor access | Notes |
|---|---|---|
admin |
Full editor + admin portal | Manages users, SSO, TOTP, security settings |
author |
Full editor | Day-to-day content authoring |
presenter |
Library + launch view | Cannot author; can launch showcases |
front_desk |
Stripped editor | Text-content widgets only (MOTD, lunch menu, welcome, banners) |
viewer |
Read-only | Guest / kiosk read access |
Database
The SQLite database file is created automatically on first boot.
| Platform | Path |
|---|---|
| Linux | /var/lib/mt-showcase/showcase.db |
| Windows | %PROGRAMDATA%\MultiTaction\Showcase\showcase.db |
Backup: stop the server, copy showcase.db to your backup target,
restart the server. Also back up the KEK (see
SHOWCASE_AUTH_KEK below).
Restore: stop the server, replace showcase.db with the backup, restart
the server. The KEK must match the one that was active when the backup was
made, or encrypted secrets (TOTP, SSO client secrets, JWT signing keys) will
be unreadable.
Environment variables
Configuration precedence (highest first): CLI flags > environment variables > YAML config file > built-in defaults.
| Variable | Default | Purpose |
|---|---|---|
SHOWCASE_EDITOR_PORT |
8080 |
HTTP port for the editor and API. Override with --port. |
SHOWCASE_HTTP_HOST |
0.0.0.0 |
HTTP bind address. Override with --host. |
SHOWCASE_CONTROL_PORT |
9299 |
TCP control server port (C++ player connection). Override with --control-port. |
SHOWCASE_CONTROL_HOST |
0.0.0.0 |
TCP control server bind address. Override with --control-host. |
SHOWCASE_AUTH_KEK |
(auto-generated) | 32-byte AES-256 key-encryption key, base64-encoded. See note below. |
SMTP_HOST |
'' |
Mailer hostname. Empty = no real email sent (dev/test only). |
SMTP_PORT |
587 |
SMTP port. STARTTLS by default. |
SMTP_SECURE |
false |
true = implicit TLS (port 465). false = STARTTLS. |
SMTP_USER |
'' |
SMTP auth username. Empty = no auth. |
SMTP_PASSWORD |
'' |
SMTP auth password. |
SMTP_FROM |
noreply@localhost |
From: address for invite, reset, and magic-link emails. |
NODE_ENV |
development_sqlite |
Selects the YAML config file to load. Set to production for production deployments. |
SHOWCASE_AUTH_KEK
The KEK (key-encryption key) protects secrets stored in SQLite: TOTP shared secrets, JWT signing keys, and SSO provider client secrets.
If SHOWCASE_AUTH_KEK is not set, the server generates a per-install KEK at
<serverRoot>/.auth-kek (mode 0600) on first boot.
Back up the KEK
Losing .auth-kek (or the SHOWCASE_AUTH_KEK env var value) makes all
encrypted secrets unrecoverable. TOTP users would need to re-enrol, SSO
providers would need to be re-configured, and the JWT signing key would
need rotation. Back up .auth-kek alongside showcase.db.
To set the KEK explicitly (recommended for production):
export SHOWCASE_AUTH_KEK="$(openssl rand -base64 32)"
Store the value in your secrets manager and set it via environment variable before starting the server.
showcase-server CLI reference
showcase-server <subcommand> [flags] opens the configured database, runs
any pending migrations, performs the action, writes an audit log row, and
exits. The server daemon does not start when a subcommand is provided.
Use CLI subcommands for recovery operations that must not depend on a working browser session (lost admin password, locked account, etc.).
users:create-admin
showcase-server users:create-admin --email <email> --password <password> [--display-name <name>]
Creates a role=admin status=active user. Bypasses the setup wizard.
- Idempotent when the email already belongs to an active admin (exit 0, no change).
- Refused (exit 65) when the email exists with a different role or status — the CLI does not silently promote or demote.
- Password must satisfy the standard policy (12+ chars, one letter, one digit). Weak passwords exit 64.
Example:
showcase-server users:create-admin \
--email alice@example.com \
--password 'SecurePassword123' \
--display-name Alice
users:reset-password
showcase-server users:reset-password --email <email> --password <new-password>
Overwrites the user's password hash and clears any active lockout
(failed_login_count, locked_until). Exits 66 if the user is not found.
users:disable / users:enable
showcase-server users:disable --email <email>
showcase-server users:enable --email <email>
Toggle the user's status between active and disabled. Disabled users
cannot log in. Both commands are idempotent (no-op if status already matches).
users:unlock
showcase-server users:unlock --email <email>
Clears failed_login_count and locked_until for a user who has tripped the
lockout threshold. Equivalent to the Unlock button in the admin UI.
Idempotent.
keys:rotate-signing
showcase-server keys:rotate-signing
Mints a new JWT signing key and demotes the previous key to a 30-day validity window. Already-issued access tokens continue to verify until they expire naturally. Prints the new key ID on success.
For immediate fleet-wide session invalidation (e.g. suspected compromise), use the admin UI instead: Admin → Security → Revoke all sessions. That operation revokes all refresh tokens AND rotates the signing key in a single transaction.
CLI exit codes
| Code | Meaning |
|---|---|
| 0 | Success (or no-op) |
| 1 | Unhandled error |
| 64 | Usage / validation error |
| 65 | Email collision with conflicting role or status |
| 66 | User not found |
SSO configuration
Admins manage SSO providers via Admin → SSO in the editor. Each provider's client secret is encrypted at rest under the KEK.
OIDC (example: Google Workspace)
In the admin SSO page, create a provider with kind oidc and the following
fields:
| Field | Example value |
|---|---|
| Name (URL slug) | google |
| Display name | Sign in with Google |
| Issuer | https://accounts.google.com |
| Client ID | <your-client-id>.apps.googleusercontent.com |
| Client secret | <your-client-secret> |
| Redirect URI | https://<your-server>/api/auth/sso/google/callback |
| Scopes | openid email profile |
| Auto-provision | off (recommended) |
| Default role | viewer |
The login button appears on the editor login page as the display name. The
callback maps the OIDC email claim to a users.email row. When
auto-provision is off, unrecognised emails are refused; when on, a new
account is created with the default role.
SAML (example: Okta / Azure AD)
| Field | Example value |
|---|---|
| Name (URL slug) | okta |
| Display name | Sign in with Okta |
| Entity ID | https://<your-server>/saml/metadata |
| SSO URL | https://<idp>/sso/saml |
| IdP certificate | PEM block from IdP metadata |
| Redirect URI | https://<your-server>/api/auth/sso/okta/callback |
| Email attribute | email |
| Display name attribute | displayName |
| Auto-provision | off (recommended) |
| Default role | viewer |
Configure your IdP to POST a SAML Response to the redirect URI with the
email attribute populated. The callback maps email to users.email.
TOTP (two-factor authentication)
TOTP is off by default. To require it instance-wide:
- Go to Admin → Security → Require TOTP for all users and enable it
(or
PATCH /api/admin/security/settings { "totpRequired": true }). - On their next login each user is redirected to enrol: scan the QR code in their authenticator app and confirm a 6-digit code.
- After enrolment, every login includes a TOTP challenge step.
Backup codes are issued once at enrolment (10 single-use codes). They cannot be re-displayed — users must store them at enrolment time.
To reset TOTP for a user who has lost their device, use the admin UI: Admin → Users → [user] → Reset TOTP. The user re-enrols on next login.
Lockout configuration
After N failed login attempts within a sliding window, the account is locked
for a configurable duration. Configure via Admin → Security or
PATCH /api/admin/security/settings:
| Setting | Default | Range | Description |
|---|---|---|---|
| Lockout threshold | 5 | 1–50 | Failed attempts before lockout |
| Lockout window | 15 minutes | 1–1440 min | Window over which attempts count |
| Lockout duration | 30 minutes | 1–1440 min | How long the lockout lasts |
To unlock a user manually: Admin → Users → [user] → Unlock, or:
showcase-server users:unlock --email <email>
Fleet session revocation
To immediately invalidate every active session across the fleet (for example, after a suspected credential compromise):
Admin → Security → Revoke all sessions
This revokes all refresh tokens and rotates the JWT signing key in a single transaction. Every browser session will fail its next token refresh and redirect to login. Already-issued access tokens remain valid for up to 15 minutes until they expire naturally.
To rotate the signing key only (without revoking refresh tokens):
showcase-server keys:rotate-signing
Troubleshooting
Cannot log in — admin account locked out
Run from the server host:
showcase-server users:unlock --email admin@local
# If you also need to reset the password:
showcase-server users:reset-password --email admin@local --password 'NewPassword123'
Setup wizard gone but no admin account exists
The setup wizard returns 410 Gone once any user exists in the database. If
that user is unreachable, create a recovery admin via CLI:
showcase-server users:create-admin \
--email recovery@local \
--password 'RecoveryPassword123'
"Cannot decrypt: KEK mismatch" in server logs
The SHOWCASE_AUTH_KEK env var or .auth-kek file has changed since the
database secrets were encrypted. Restore the original KEK value. If the
original is lost, affected secrets must be reset:
- TOTP: reset per-user via admin UI; users re-enrol on next login.
- SSO providers: re-enter client secrets in the admin SSO page.
- JWT signing key: run
showcase-server keys:rotate-signing.
SMTP failures — users not receiving invite / reset emails
The server returns a uniform success response to the browser even when SMTP
fails (anti-enumeration measure). Check the admin banner in the editor for
SMTP error notices, and query the audit log for mailer.send_failed rows.
Verify SMTP_HOST, SMTP_PORT, SMTP_USER, and SMTP_PASSWORD are set
correctly.
Custom plugin rejected at startup
Showcase logs [startup] rejected package <name>: incompatible Package::canBeUsed signature for any plugin compiled against 1.14.x. Rebuild the plugin against the 26.4.1 SDK; no source-code changes are required.
Refresh token reuse detected in audit log
An auth.token_reuse audit event means an old refresh token was presented
after a newer one had already been issued. The server revokes the entire
token family and forces re-authentication. This is normal stolen-token
detection behaviour — no operator action needed unless the pattern repeats
for the same user account.