Host Config & Security
Configure the permission model, action gating, plugin security, and content restrictions for the MP4E player.
Overview
MP4E uses a three-tier permission model that follows an intersection pattern: each layer can only restrict, never expand, what the previous layer allows. This ensures that no single party (creator, host, or viewer) can override the restrictions of another.
| Tier | Source | Controls |
|---|---|---|
| 1. Creator Restrictions | metadata.settings.restrictions | Author-declared limits embedded in the .mp4e file (e.g., sequential playback, blocked actions). Enforced by the engine even without host config. |
| 2. Host Config | config React prop | Sandbox actions, disable overlays/layers/plugins/rules, gate actions for approval, plugin security rules. |
| 3. Viewer Preferences | setViewerPreferences() at runtime | Privacy (doNotTrack, doNotShareIdentity, limitStorage) and accessibility (reducedMotion, highContrast). The viewer always has final say. |
// Three-tier permission model
// Each layer can only RESTRICT, never expand permissions.
// Tier 1: Creator restrictions (embedded in the .mp4e file)
metadata.settings.restrictions = {
sequential: true, // No seeking forward
actions: { download: false }
};
// Tier 2: Host config (React prop)
<MP4EPlayer
config={{
sandbox: { actions: { openUrl: false } },
gated: ['navigate'],
disabled: { layers: ['debug-layer'] }
}}
/>
// Tier 3: Viewer preferences (runtime, from player menu)
engine.setViewerPreferences({
doNotTrack: true,
reducedMotion: true
});download and the host blocks openUrl, then both are blocked at runtime. Neither party can re-enable what the other has restricted.MP4EConfig Reference
The config prop accepts an MP4EConfig object. It is passed to the WASM engine via setConfig() after setMetadata().
Sandbox
Block specific action types globally. Actions not listed are allowed by default.
| Prop | Type | Default | Description |
|---|---|---|---|
sandbox.actions | Record<string, boolean> | - | Block or allow specific action types. Keys are action type strings (e.g. "openUrl", "navigate"). Values: true = allowed, false = blocked. Any action type not listed is allowed by default (default-open model). |
Disabled Content
Suppress specific creator content by ID. Disabled items are silently removed from the engine's working set.
| Prop | Type | Default | Description |
|---|---|---|---|
disabled.overlays | string[] | - | Array of overlay IDs to suppress. Matching overlays will not render. |
disabled.plugins | string[] | - | Array of plugin IDs to suppress. Matching plugins will not load or render. |
disabled.layers | string[] | - | Array of layer IDs to suppress. All overlays within matching layers are hidden. |
disabled.rules | string[] | - | Array of rule IDs to suppress. Matching rules will not be evaluated or executed. |
disabled.actionTypes | string[] | - | Array of action type strings to suppress globally. Any action of a matching type will be silently dropped. |
Action Gating
Require host approval before executing certain action types. The engine pauses the action and invokes the onActionGate callback.
| Prop | Type | Default | Description |
|---|---|---|---|
gated | string[] | - | Action types that require host approval before execution. When an action of a listed type fires, the engine pauses it and invokes the onActionGate callback. The host must call resolve(true) to allow or resolve(false) to deny. |
gateTimeoutMs | number | 5000 | How long (in milliseconds) the engine waits for the host to call resolve() on a gated action before applying the timeout policy. |
gateTimeoutPolicy | 'allow' | 'deny' | 'deny' | What happens when the gate timeout expires without a resolve() call. "allow" lets the action proceed; "deny" silently drops it. |
Plugin Security
Control network access, sandbox level, and signer requirements per plugin or group of plugins using wildcard patterns.
| Prop | Type | Default | Description |
|---|---|---|---|
pluginSecurity | Record<string, PluginSecurityRule> | - | Plugin security rules keyed by wildcard pattern. Patterns match plugin namespace:identifier format. Controls network access, sandbox level, and signer requirements per plugin or group of plugins. If set but no "*" key is provided, the implicit default is "none" (block all). |
networkGateTimeoutMs | number | 5000 | How long (in milliseconds) to wait for the host to resolve a network gate request before applying the timeout policy. |
networkGateTimeoutPolicy | 'allow' | 'deny' | 'deny' | What happens when the network gate timeout expires. "allow" permits the request; "deny" blocks it. |
Trust & Signing
Shortcuts for self-hosted deployments and plugin signature verification.
| Prop | Type | Default | Description |
|---|---|---|---|
trustContent | boolean | false | Trust all plugin content unconditionally. When true, every plugin receives the "trusted" sandbox level (allow-same-origin) regardless of other pluginSecurity rules. Use only for self-hosted deployments where you control all content. |
trustedSigners | Record<string, string | true> | - | Trusted signers for plugin signature verification. Keys are signer names, values are base64-encoded Ed25519 public keys. The special value true for the key "mp4e" uses the built-in MP4E public key. |
Action Gating
Action gating lets the host intercept specific action types before they execute. This is useful for confirming navigation, preventing unwanted URL opens, or logging action execution. The flow is:
- A rule or plugin triggers a gated action (e.g.,
openUrl) - The engine pauses execution and invokes
onActionGate(action, resolve) - The host inspects the action and calls
resolve(true)to allow orresolve(false)to deny - If the host does not respond within
gateTimeoutMs, thegateTimeoutPolicyapplies
import { MP4EPlayer } from '@mp4e/react';
import '@mp4e/react/styles.css';
function SecurePlayer() {
return (
<MP4EPlayer
src="/video.mp4"
config={{
gated: ['openUrl', 'navigate'],
gateTimeoutMs: 10000,
gateTimeoutPolicy: 'deny',
}}
onActionGate={(action, resolve) => {
if (action.type === 'openUrl') {
// Show a confirmation dialog before opening external URLs
const approved = window.confirm(
`This video wants to open: ${action.params.url}\nAllow?`
);
resolve(approved);
} else if (action.type === 'navigate') {
// Always allow in-app navigation
resolve(true);
} else {
// Deny unknown gated actions
resolve(false);
}
}}
/>
);
}resolve(), the action is automatically denied (or allowed) after gateTimeoutMs based on the gateTimeoutPolicy. The default policy is "deny" (fail-closed).Network Gating
When a plugin uses mp4e.fetch() to request a domain not in its allowed list, the onNetworkGate callback fires. This gives the host final say over which external requests plugins can make.
<MP4EPlayer
src="/video.mp4"
config={{
networkGateTimeoutMs: 3000,
networkGateTimeoutPolicy: 'deny',
}}
onNetworkGate={(request, resolve) => {
// Only allow requests to your own domains
const allowed = ['api.mysite.com', 'cdn.mysite.com'];
const url = new URL(request.url);
console.log(
`Plugin ${request.pluginId} requesting ${request.method} ${request.url}`
);
resolve(allowed.includes(url.hostname));
}}
/>The callback receives the requesting plugin's ID, the full URL, and the HTTP method. Call resolve(true) to allow or resolve(false) to block the request. If the host does not respond within networkGateTimeoutMs, the networkGateTimeoutPolicy applies.
Plugin Security Rules
The pluginSecurity config field maps wildcard patterns to security rules. Patterns are matched against the namespace:identifier of each plugin.
| Pattern | Matches | Example |
|---|---|---|
'*' | All plugins (global default) | Baseline security for every plugin |
'mp4e:*' | All plugins in the mp4e namespace | Official MP4E plugins |
'stripe:checkout' | Exact plugin match | Specific plugin elevated access |
'!badguy:*' | Deny rule (overrides positive matches at equal or lower specificity) | Block an entire namespace |
pluginSecurity is set but does not include a '*' key, the implicit default is "none" (all plugins blocked). Always include a '*' entry as your baseline.PluginSecurityRule Type
interface PluginSecurityRule {
/**
* Network access policy:
* - 'none': Block all network access
* - 'manifest': Allow only domains declared in plugin manifest
* - 'any': Unrestricted (dangerous, explicit opt-in only)
* - string[]: Explicit domain allowlist (replaces manifest)
*/
network?: 'none' | 'manifest' | 'any' | string[];
/**
* Sandbox level controlling iframe capabilities:
* - 'contained': allow-scripts, allow-forms only (NO same-origin)
* - 'connected': same + mp4e.fetch() + bridge storage
* - 'trusted': + allow-same-origin (full host page access)
*/
sandbox?: 'contained' | 'connected' | 'trusted';
/** Required signer name -- plugin must be signed by this signer to render */
signer?: string;
}Full Example
<MP4EPlayer
src="/video.mp4"
config={{
pluginSecurity: {
// Global default: contained sandbox, no network
'*': {
sandbox: 'contained',
network: 'none',
},
// Official MP4E plugins: connected sandbox, manifest-declared domains
'mp4e:*': {
sandbox: 'connected',
network: 'manifest',
signer: 'mp4e',
},
// Stripe plugins: connected sandbox, explicit domain allowlist
'stripe:*': {
sandbox: 'connected',
network: ['js.stripe.com', 'api.stripe.com'],
},
// Specific trusted plugin: full access
'stripe:checkout': {
sandbox: 'trusted',
network: 'any',
},
// Block a known-bad namespace entirely
'!badguy:*': {
sandbox: 'contained',
network: 'none',
},
},
}}
/>Content Restrictions
Content restrictions are creator-declared and embedded in metadata.settings.restrictions. They are the first tier of the permission model and are enforced by the engine even when no host config is provided. Think of them like SCORM sequencing rules.
MP4ERestrictions
| Prop | Type | Default | Description |
|---|---|---|---|
actions | Record<string, boolean> | - | Action types the creator wants blocked. Same shape as config.sandbox.actions. Blocked actions are silently dropped by the engine. |
sequential | boolean | false | Force linear playback. When true, the viewer cannot seek forward past the furthest-watched point. Similar to SCORM's forwardOnly mode. |
// Creator embeds restrictions in the video metadata
// These are enforced by the engine regardless of host config
{
"settings": {
"restrictions": {
"sequential": true,
"actions": {
"download": false,
"openUrl": false
}
}
}
}sequential is enabled, the engine tracks the furthest-watched point and prevents seeking beyond it. This is commonly used for e-learning and compliance training videos where linear completion is required.Security Violations
The onSecurityViolation callback fires when a plugin attempts a blocked network request (CSP violation). Use it for monitoring and alerting.
<MP4EPlayer
src="/video.mp4"
onSecurityViolation={(violation) => {
console.error(
`Security violation by plugin ${violation.pluginId}:\n` +
` Blocked URI: ${violation.blockedURI}\n` +
` Violated directive: ${violation.violatedDirective}`
);
// Report to your monitoring service
analytics.track('security_violation', {
pluginId: violation.pluginId,
blockedURI: violation.blockedURI,
directive: violation.violatedDirective,
});
}}
/>| Field | Type | Description |
|---|---|---|
pluginId | string | The ID of the plugin that triggered the violation. |
blockedURI | string | The URI that was blocked by the Content Security Policy. |
violatedDirective | string | The CSP directive that was violated (e.g., "connect-src", "script-src"). |
Interactive Tool
MP4EConfig object. Select action types to sandbox, choose plugins to disable, configure gating policies, and set up plugin security rules -- then copy the generated config directly into your code.Full Production Example
Putting it all together -- a production-ready player with sandbox rules, action gating, plugin security, and violation monitoring:
import { MP4EPlayer } from '@mp4e/react';
import '@mp4e/react/styles.css';
function ProductionPlayer({ videoUrl }: { videoUrl: string }) {
return (
<MP4EPlayer
src={videoUrl}
config={{
// Block dangerous actions entirely
sandbox: {
actions: {
download: false,
openUrl: false,
},
},
// Require approval for navigation
gated: ['navigate', 'goToScene'],
gateTimeoutMs: 8000,
gateTimeoutPolicy: 'deny',
// Disable specific creator content
disabled: {
layers: ['debug-layer'],
actionTypes: ['console'],
},
// Plugin security per namespace
pluginSecurity: {
'*': { sandbox: 'contained', network: 'none' },
'mp4e:*': { sandbox: 'connected', network: 'manifest' },
'stripe:checkout': { sandbox: 'trusted', network: 'any' },
},
// Trust official MP4E signatures
trustedSigners: { mp4e: true },
}}
// Gate callbacks
onActionGate={(action, resolve) => {
resolve(window.confirm(`Allow action: ${action.type}?`));
}}
onNetworkGate={(req, resolve) => {
const trusted = ['api.mysite.com', 'cdn.mysite.com'];
resolve(trusted.includes(new URL(req.url).hostname));
}}
onSecurityViolation={(v) => {
console.error('Security violation:', v);
}}
/>
);
}