# Toolbox Portal requirements av-tools is built and packaged independently. Toolbox Portal consumes the exact ZIP and SHA-256 from a reviewed release lock; it must never import the app source into its React build. ## Required serving behavior All app, wrapper, core, WASM and worker responses are same-origin and served over HTTPS (or trusted localhost). Apply: ```text Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp Cross-Origin-Resource-Policy: same-origin X-Content-Type-Options: nosniff ``` Serve `.wasm` as `application/wasm`, JavaScript modules as a JavaScript MIME type and JSON as `application/json`. Test real responses under `/apps/av/vendor/ffmpeg/0.12.10/...`; a file-extension allowlist alone does not prove the MIME type. Suggested CSP: ```text default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; worker-src 'self' blob:; connect-src 'self'; img-src 'self' blob: data:; media-src 'self' blob:; font-src 'self'; style-src 'self'; object-src 'none'; frame-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'; ``` `'wasm-unsafe-eval'` is the narrow WebAssembly permission; broad `'unsafe-eval'` is unnecessary. `worker-src blob:` covers the wrapper-created worker path. `media-src blob:` permits local source/result previews. ## Immutable reference and active-worktree state Portal revision `bda9da044d61c109afd9969d357cc27f76648938` already sends COOP `same-origin`, COEP `require-corp`, CORP `same-origin`, `script-src 'self' 'wasm-unsafe-eval'`, and `worker-src 'self' blob:`. Its packaged nginx CSP does **not** declare `media-src`; `default-src 'self'` therefore blocks local `blob:` audio/video URLs. The expected browser diagnostic is equivalent to: ```text Refused to load media from 'blob:…' because it violates "default-src 'self'". ``` Smallest required Portal change: append exactly `media-src 'self' blob:;` to that CSP. This permits only same-origin and in-memory Blob media; it does not permit remote media hosts, inline script or eval. If the Portal is not changed, conversion/download can remain available but Blob source/result preview must show a clear disabled reason. As inspected on 2026-07-24, the active `/mnt/DATA/git/toolbox-portal/deploy/nginx.conf` contains that exact directive as a local worktree patch. The worktree still resolves to the immutable revision above, so the patch is useful for local deployment testing but is not a committed Portal revision and must not be cited as published release evidence. The clean-reference assembly smoke therefore continues to require `--allow-known-header-gap` until a reviewed Portal commit contains the fix. The nginx profile includes `.wasm` in its static allowlist/gzip types but relies on the base image `mime.types`. Verify `Content-Type: application/wasm` against the pinned image; add an explicit mapping if the response differs. ## Caching The immutable Portal reference's cache map makes Vite content-hashed assets immutable but leaves versioned FFmpeg cores at `no-cache`. Correctness is unaffected. For performance, the exact directory `apps/av/vendor/ffmpeg/0.12.10/{st,mt}/` may receive: ```text Cache-Control: public, max-age=31536000, immutable ``` Only do this while a version path is immutable and verified against `version.json`. Keep `index.html`, manifest/catalog/release JSON and legal notices revalidated. ## Nested path and context The artifact uses `base: './'`, `entry: './'`, a relative icon, a relative manifest URL and relative core URLs. Portal may therefore target `av`, yielding `apps/av/`. Do not rewrite files to root-absolute `/assets` or `/vendor`. Use top-level navigation; embedding is unsupported. The app remains usable without valid Toolbox context. It validates context version/origin and falls back to standalone shell behavior without moving or duplicating suite controls. ## Fallback contract MT requires all isolation headers plus secure context and `SharedArrayBuffer`. When any prerequisite or MT load fails, the app terminates that instance and loads ST. The manifest intentionally declares `crossOriginIsolated: false` because isolation is not an unconditional requirement; it declares workers, secure context, IndexedDB and top-level context. See `scripts/portal-assembly-smoke.mjs` and `docs/RELEASE.md` for the temporary, non-mutating assembly procedure.