Capabilities
Media
Introduction
Vimeo and YouTube embeds are a routine requirement, and they need no capability. What they do need is a declaration: your applet names a video by id, and the Rack builds the player URL.
Everything else, meaning your own images, audio and video, ships inside the zip and is referenced relatively. The Content Security Policy allows img-src and media-src of 'self' data: blob: plus whatever hosts the deployment has allowlisted, and nothing else.
Declaring Media
To get started, declare each embed in your manifest's media array:
"media": [
{ "id": "corine.diagnosis", "provider": "vimeo", "key": "912345678" },
{ "id": "intro.welcome", "provider": "youtube", "key": "dQw4w9WgXcQ" }
]Each embed accepts the following fields:
| Field | Rules |
|---|---|
id |
Required. ^[a-z0-9][a-z0-9_.:-]*$, max 150, unique. |
provider |
Required. A provider the Rack supports: vimeo or youtube. |
key |
Required. ^\d+$ for Vimeo, ^[\w-]{11}$ for YouTube. |
The Rack validates these at upload, not at runtime, so a typo in a video id fails the package rather than the participant's session. A Rack may also look up the video's title, thumbnail and duration from the provider at install time and cache them for your applet, so your applet never needs to talk to the provider at runtime.
Rendering a Player
You may ask for a declared embed's player URL with the embedUrl method, and for the whole embed, including any cached metadata, with the get method:
const url = Rack.media.embedUrl('corine.diagnosis');
if (url) {
frame.src = url;
}
const embed = Rack.media.get('corine.diagnosis');
embed?.meta?.title; // cached oEmbed metadata, when resolved
embed?.meta?.duration;
Rack.media.all(); // every declared embed, by idThese reads are synchronous, because the embeds arrive in launch data.
Your nested player frame should carry the permissions the player needs:
<iframe id="player" allow="autoplay; fullscreen; picture-in-picture; encrypted-media"></iframe>The Rack sets a matching allow on your applet's own iframe. Permissions Policy is chained through both frames, so a player frame without these permissions silently loses autoplay and fullscreen, which is a genuinely hard bug to diagnose from inside the applet.
Nested players inherit your applet's sandbox flags, so the player also runs with an opaque origin, with no cookies and no storage of its own. Both providers' JavaScript APIs are postMessage-driven, so they continue to work. A nested frame cannot talk to the Rack, however; only your applet's own frame can.
Privacy Defaults
The Rack builds YouTube URLs against youtube-nocookie.com, and Vimeo URLs with dnt=1. This is not configurable per applet, so don't build a player URL yourself; use the one embedUrl() returns.