TL;DR — Blink AI micro-trainings now run content authored elsewhere — SCORM packages since September, H5P activities since October. Every package is untrusted code: validated on upload, served from signed URLs, played in a sandbox that cannot read our cookies. The SCORM player took production down twice before it worked.
A coaching product is easy to secure when it only runs code we wrote. But L&D teams already own courses from authoring tools, and rebuilding them is a non-starter. This is part 2 of our product notes — part 1 covered Wallnetic 1.4. Here is what is new in Blink AI:
- SCORM player (September). SCORM 1.2 and 2004 packages run as a step in the learner’s chat, recording the package’s own status and score.
- H5P activities (October). Interactive packages play as a chat card — in the mobile app too, without an app update — and unlock the next step on their xAPI signal, or on load for types that send none.
- cmi5 export (October). Micro-trainings also export as cmi5 (xAPI) packages, next to SCORM and AICC.
- LMS frame fix (August). Partitioned cookies keep LMS launches signed in.
Upload is the first boundary
Uploads are role-gated and size-capped; every package passes these checks before it can play:
- Path escape — entries that resolve outside the package root are never unpacked.
- Zip bombs — caps on entry count and unpacked size.
- Manifests — the SCORM launch file comes from the manifest, never a guess; an H5P package must resolve every library it needs.
- File types — one file outside H5P’s allow-list rejects the package, and the admin sees why.
The player sandbox
- No same-origin. The frame gets
sandbox="allow-scripts"withoutallow-same-origin— the pair is a classic escape — so it runs in an opaque origin that cannot read our cookies. - A bridge, not a parent. A sandboxed SCORM package cannot find its API in
window.parent, so an injected shim relays calls overpostMessage, checked byevent.source—event.originis always"null"here. - Signed, expiring URLs. Assets load through HMAC-signed URLs scoped to one package; H5P signs with its own key.
- Path-scoped content security policy. An H5P frame may load the runtime and its own files — nothing else from our API.
Shared H5P libraries, separate tenants
Many H5P exports omit their JavaScript libraries, so we keep a shared store that only a full administrator can add to, for example from the H5P Hub. In a multi-tenant product, that store — and anything fetched from outside — is the dangerous part:
- Uploads never write to it. Otherwise one organisation’s package could ship code into another’s activities. Libraries are copied into each package, so store updates never change live content.
- External media is imported. The server copies linked media files into the package where possible, so learners’ browsers do not fetch them from third parties. The fetcher checks the resolved IP at connect time, redirects included.
Launched from someone else’s LMS
In the other direction, a customer’s LMS launches Blink AI, and cmi5 support there is uneven. The export keeps trust in one place:
- No secret in the package. The zip holds only
cmi5.xml; the assignable unit runs on our server. - The LMS vouches for the learner. We read
LMS.LaunchDatafrom the LMS’s learning record store (LRS) with its one-time token — a document that exists only if the LMS launched this learner into this package. - Pinned and scoped. The LRS endpoint is pinned on first launch, and learner IDs hash scope, endpoint and LMS account.
What was hard
- A route that only failed in the container. The player’s wildcard route,
/:contentId/play/*, worked locally, but the Docker image resolvedpath-to-regexpv8, where a bare*is illegal. Production went down twice in one day while we chased hypotheses without logs; with logs, the cause was one line. A test now checks every route pattern. - Each fix uncovered the next. Frame headers, cookieless asset requests, then a
Cross-Origin-Resource-Policyheader blocking responses that had come back 200. - A cookie our server never saw. LMS launches showed a blank screen while every log looked clean: browsers blocking third-party cookies dropped our auth cookie inside the frame. Redirects would break the SCORM bridge and the Storage Access API needs a gesture, so the fix was a
Partitioned(CHIPS) cookie.
What this buys
L&D teams can bring courses they already own into the coach’s flow — which matters in a pilot, where nobody waits for a rebuild. The outage lesson holds: logs first, hypotheses second. Next in product notes: back to Wallnetic — the state machine behind “who paused it?”.
Frequently asked questions
Can Blink AI play SCORM and H5P content from other authoring tools?
Yes. Admins can upload SCORM 1.2 or 2004 packages and H5P activities and add them to a micro-training as steps. They play inside the learner’s chat in a sandboxed frame, completion is recorded, and the flow moves on. External media files that an H5P package links to are copied in where possible.
Does Blink AI support cmi5 export to an LMS?
Yes. Alongside the SCORM 1.2, SCORM 2004 and AICC exports, admins can download a cmi5 (xAPI) package. It contains only cmi5.xml and no secret; each launch is verified against the LMS’s own learning record store. One known limit: a package is tied to the first LRS that opens it.
Working on something similar?
T-Square architects, builds and operates production systems for learning, AI and custom software products. Talk to a senior engineer for a second opinion.