Skip to content

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_PORT or --port if 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

  1. Download the 26.4.1 installer (.deb for Linux, .exe for Windows) from the Showcase downloads page.
  2. Run the installer on each Showcase node.
  3. Start the server. Confirm the startup log line:
    [startup] HTTP server listening on 0.0.0.0:8080
    

Step 4: Complete the setup wizard

  1. Open the editor URL in a browser (http://<server>:8080). You will land on /setup — the first-boot setup wizard.
  2. Complete the form:
    • Email address
    • Display name
    • Password (minimum 12 characters; at least one letter and one digit)
  3. The server creates a role=admin status=active account and redirects to /login.
  4. 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:

  1. Go to Admin → Security → Require TOTP for all users and enable it (or PATCH /api/admin/security/settings { "totpRequired": true }).
  2. On their next login each user is redirected to enrol: scan the QR code in their authenticator app and confirm a 6-digit code.
  3. 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'
This command is idempotent on email match.

"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.