Lab Kiosk OS & Edge SaaS

User Portal

The User Portal is what a user sees when a workstation is idle: a grid of application cards curated by their operator, served from their organization's own subdomain.

It is rendered by src/ui_portal.ts at /home on a tenant host. The root / serves the organization homepage (src/ui_org_home.ts) instead, and /portal was removed.


What a user sees

+---------------------------------------------------------------+
|  Digital Learning Lab                          [portal_title] |
|  Select an approved page to begin         [portal_subtitle] |
|                                                               |
|  Math & Science                                    [category] |
|  +-------------+  +-------------+  +-------------+            |
|  |  ๐ŸŽ“          |  |  ๐Ÿ”ฌ          |  |  ๐Ÿ“          |            |
|  | Khan Academy|  |  PhET Sims  |  |  GeoGebra   |            |
|  +-------------+  +-------------+  +-------------+            |
|                                                               |
|  Computer Science                                             |
|  +-------------+  +-------------+                             |
|  |  ๐Ÿฑ          |  |  ๐Ÿ’ป          |                             |
|  |   Scratch   |  |  Code.org   |                             |
|  +-------------+  +-------------+                             |
|                                                               |
|  For technical assistance, raise your hand.    [portal_footer]|
+---------------------------------------------------------------+

Cards are grouped by category and ordered by order_index. Everything on the page โ€” title, subtitle, description, footer โ€” is per-organization copy the operator controls.

There is no search box, no address bar, and no way to reach anything that is not on this page or on the allowlist.


Where the cards come from

GET /api/portal-sites is public, scoped to the host tenant. A card is:

FieldPurpose
titleThe card's label
urlWhere it goes
domainDerived from the URL; feeds the effective allowlist
categoryGroups the cards
iconAn emoji
thumbnail_urlOptional card image
order_indexDisplay order
is_activeWhether it renders

Operators manage them under Portal Apps in the dashboard. โ†’ Admin Console Guide


Adding a card authorises its domain

This is the part worth understanding. The allowlist a workstation enforces is not just the permanent list an operator typed in โ€” it is that list unioned with every portal app's host, computed per heartbeat by buildEffectiveWhitelist().

tenant_whitelist rows        khanacademy.org, example.com
      +
portal_sites.domain          scratch.mit.edu, phet.colorado.edu
      +
active broadcast host        (when one is active)
      =
effective allowlist          delivered in every telemetry response

So an operator who adds a Scratch card does not also have to add scratch.mit.edu to the allowlist. A card that appeared but did not work would be an obvious and constant support burden, and this removes it.


Clicking a card navigates top-level, natively. It is not an iframe โ€” Khan Academy, YouTube, and Scratch all enforce X-Frame-Options: SAMEORIGIN and would fail with ERR_BLOCKED_BY_RESPONSE.

Once on the site, the kiosk's own chrome comes from the browser extension: an auto-hiding 44 px bar with Home, Back, Forward, Reload, and a status shield, injected into a closed Shadow DOM.

Home returns to the portal. That is the user's only reliable route back, which is why the bar exists at all.

โ†’ Browser Extension


When a broadcast is active

A broadcast overrides the portal. Workstations navigate to the operator's URL and stay there: the URL is stored on the tenant row, so a machine that reboots mid-page rejoins the same page.

At the broadcast root the extension suppresses Alt+Left and Backspace, so a user cannot reverse out of it. The operator's Reset to portal clears the broadcast and returns everyone to the launcher.


single_url mode

An organization can skip the portal entirely. In single_url mode workstations go to one destination โ€” an LMS, an assessment platform, a library catalogue โ€” and there is no launcher at all.

Use portal mode when users choose among approved resources; single_url when they should be in exactly one place.


Customisation

SettingWhere it appears
portal_titleThe main heading
portal_subtitleUnder the heading
portal_descriptionRoom or context label, e.g. "Computer Science Lab 304"
portal_footerBottom of the page, e.g. "For technical assistance, raise your hand."
Organization nameThroughout

Set under Settings โ†’ Customization.


Escaping

Every one of those strings, and every card title and URL, is attacker-controlled from the platform's perspective: it arrives through registration or the admin console.

  • Server-side, each interpolation goes through escapeHtml() or escapeJson().
  • Client-side, DOM nodes are built and textContent assigned โ€” never concatenation into innerHTML.
  • URLs pass safeHttpUrl(); a card whose URL is not http(s) is rejected on creation.

The test suite renders the portal with a hostile app title and asserts it comes back inert.


Privacy

A user's session leaves nothing behind. overlayroot="tmpfs" sends every write to RAM: browser profile, cache, history, downloads, and any file they saved. At power-off it is all gone, and the next user gets a genuinely fresh machine.

What the organization does see is a screen thumbnail every three seconds while a workstation is powered on, visible to any organization admin of that organization, plus the currently active URL. That is the supervision the product exists to provide โ€” worth being explicit about in a lab's acceptable-use policy.

โ†’ Admin Console Guide ยท Kiosk Hardening ยท Control Plane Internals

This page is wiki/User-Portal.md in the repository.