diff --git a/README.md b/README.md index aadd39a..1d4a4b9 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Full examples: - [`apps/p2p-chat`](./apps/p2p-counter/README.md): Direct sandbox-to-sandbox chat demo. - [`apps/p2p-counter`](./apps/p2p-counter/README.md): Direct sandbox-to-sandbox communication demo. - [`apps/fs-experiment`](./apps/fs-experiment/README.md): File system & storage isolation with TurboModule substitutions. +- [`apps/origin-pooling`](./apps/origin-pooling/): Origin sharing, idle TTL, and per-surface messaging demo. ## πŸ“š API Reference diff --git a/apps/origin-pooling/App.tsx b/apps/origin-pooling/App.tsx index e3ff4b0..dfcf3a5 100644 --- a/apps/origin-pooling/App.tsx +++ b/apps/origin-pooling/App.tsx @@ -2,8 +2,13 @@ * Origin Pooling Demo * * Dynamically add/remove sandboxes under two shared origins (alpha, beta) - * plus an isolated (no-origin) option. Same-origin sandboxes share a - * ReactHost / Hermes VM; removing the last one triggers the idle TTL. + * plus isolated sandboxes (each gets a unique origin and its own VM). + * Same-origin sandboxes share a ReactHost / Hermes VM; removing the last + * one triggers the idle TTL. + * + * Access control demo: alpha and beta only accept messages from "isolated-1". + * Other isolated sandboxes (isolated-2, isolated-3, …) will get + * AccessDeniedError when trying to ping alpha or beta. * * Messaging is handled inside the sandbox widget via globalThis.postMessage. * The host only logs messages received via onMessage. @@ -25,13 +30,21 @@ type SandboxEntry = {key: string; label: string; origin: string} type LogEntry = {source: string; text: string; ts: number} let nextId = 0 +let nextIsolatedId = 0 const ORIGIN_ALPHA = 'alpha' const ORIGIN_BETA = 'beta' +const ISOLATED_PREFIX = 'isolated-' const COLOR_ALPHA = '#8232ff' const COLOR_BETA = '#e67e22' const COLOR_ISOLATED = '#6c757d' +/** + * Only "isolated-1" is permitted to send messages to alpha/beta. + * All other isolated origins will be denied. + */ +const PERMITTED_ISOLATED = `${ISOLATED_PREFIX}1` + /** Alpha uses a function-based TTL (4 seconds) */ const ALPHA_TTL = () => 4000 /** Beta and isolated use a static TTL (2 seconds) */ @@ -48,7 +61,14 @@ export default function App() { const addSandbox = useCallback((origin: string) => { const id = String(++nextId) - setSandboxes(prev => [...prev, {key: id, label: `#${id}`, origin}]) + const actualOrigin = + origin === ISOLATED_PREFIX + ? `${ISOLATED_PREFIX}${++nextIsolatedId}` + : origin + setSandboxes(prev => [ + ...prev, + {key: id, label: `#${id}`, origin: actualOrigin}, + ]) }, []) const removeSandbox = useCallback((key: string) => { @@ -59,14 +79,15 @@ export default function App() { const alphas = sandboxes.filter(s => s.origin === ORIGIN_ALPHA) const betas = sandboxes.filter(s => s.origin === ORIGIN_BETA) - const isolated = sandboxes.filter(s => s.origin === '') + const isolated = sandboxes.filter(s => s.origin.startsWith(ISOLATED_PREFIX)) return ( Origin Pooling Demo - Same-origin sandboxes share a VM. Alpha: function-based TTL (4s). Beta: - static TTL (2s). + Same-origin sandboxes share a VM. Isolated sandboxes each get a unique + origin (own VM). Only isolated-1 can message alpha/beta β€” others get + AccessDeniedError. @@ -83,7 +104,7 @@ export default function App() {