Repository navigation
Conversation
Every successful render now writes <output>.hf-render.json after the artifact commits: tool versions (producer/node/ffmpeg), entry sha256, compiled-composition hash, @font-face families, variables hash (never raw values), format/fps/resolution/duration, encoder facts, output sha256+size, stage timings, worker count, HDR flag, and warning codes. - Default ON; --no-provenance (or --provenance false) disables, --provenance <path> relocates. Batch rows each write their own sidecar; --docker forwards the disable flag into the container. - JSON Schema published at packages/core/schemas/hf-render-sidecar.json. - Docs at docs/reference/render-provenance.mdx. - Sidecar write failures log a warning and never un-complete a render.
…+ rename Addresses the CodeQL insecure-temporary-file finding: the receipt is now written to a sibling .tmp file in the sidecar's own destination directory (never the shared, world-writable os.tmpdir()), fsynced, then renamed over the final path. Readers can only observe a complete receipt and there is no cross-device or symlink-swap window between write and publish. Failed renames clean up the temp file. Test asserts no .tmp residue remains.
miguel-heygen
left a comment
There was a problem hiding this comment.
Thanks for picking this back up after #3758, and sorry it has waited. The sidecar module is careful: it writes to a temp file beside the destination and renames it, a write failure never fails a render that already committed, and it keeps host paths and raw variable values out.
One concrete problem, plus two things that need a decision or a refresh:
- Studio leaves the sidecar behind when a render is deleted. Both Studio render adapters call
createRenderJobwithout a provenance setting (packages/cli/src/server/studioServer.ts:630,packages/studio/vite.adapter.ts:346), so with the default on, every Studio render also writesrenders/<jobId>.mp4.hf-render.json. The delete route only removes<jobId>with.mp4,.webm,.movand.meta.json(packages/studio-server/src/routes/render.ts:24-36), so the sidecar stays after the user deletes the render. The listing filters by video extension, so these files pile up where nobody can see them. Either add the sidecar suffix to the delete list or passprovenance: falsefrom the Studio adapters. A route test that renders, deletes and checks the folder is empty would pin it. - Rebase needed. The branch is far behind
main, andrenderOrchestrator.tshas changed a lot since the last CI run. It merges without conflicts, but it needs a fresh CI run on the current base. - Default on is a maintainer call. Writing an extra file next to every output, including project
renders/folders and every batch row, is a user-visible change. Hashing the whole output also adds a full read of the file before the render reports complete, which is noticeable on long or 4K renders. I'd like a maintainer to confirm on-by-default before this merges; opt-in would avoid both concerns.
Verdict: REQUEST_CHANGES
Reasoning: Deleting a render in Studio leaves the new sidecar file behind, and the branch needs a rebase and CI run on current main; whether the sidecar is on by default is for maintainers to decide.
Studio renders now write `<jobId>.<ext>.hf-render.json` beside the output, but DELETE /render/:jobId only removed the video files and `.meta.json`, so the sidecar stayed behind where the render list never shows it. Add the sidecar names for each video extension to the delete list, and add a route test that renders, deletes and checks the renders folder is empty.
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
|
Good catch on the Studio delete path. 6b6f8c2 adds The branch also merges current On default-on versus opt-in, I left it as is for now since that's a maintainer call. If you'd rather ship it opt-in, I can flip the default so the sidecar and the hash only happen when asked for. |
miguel-heygen
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround. The delete fix is right: the sidecar names are in the delete list and the new route test checks that the renders/ folder ends up empty. Thanks for merging main too.
Two checks are now red because of this branch, plus one smaller leftover:
- Build: producer package size.
verify:packed-manifestsreports@hyperframes/producerat 780,121 bytes packed against a budget of 768,210 (more than 1% over), and the same for unpacked. Build passes on the base commit, so it's the new sidecar module. Either trim what ships in the tarball or raise the budget inscripts/package-size-budgets.jsonand say why in the PR description, as the script's message asks. - Comments: comment share. The check flags
packages/studio-server/src/routes/render.tsbecause the new comment line aboveRENDER_SIDECAR_EXTENSIONSraises the file's comment share. The constant's name already says what it is, so removing the comment should clear it. - Cancel can still leave the sidecar behind. Both Studio adapters clean up a cancelled render by removing only the output and
.meta.json(packages/cli/src/server/studioServer.ts:647,packages/studio/vite.adapter.ts:325). The sidecar is written after the output is committed but before the job reports complete, and that step hashes the whole output, which takes a moment on a long or 4K render. If the user cancels during that hash, the adapter sees the abort, removes the video and meta, and the sidecar stays where neither the render list nor delete can reach it. Adding the sidecar suffix to both cancel cleanups, ideally from the same list the delete route uses, closes it.
The "Studio: edit accuracy gate" failure isn't yours: its shards are skipped on fork branches, so the gate has no results to report.
On default-on versus opt-in, thanks for offering to flip it. That's still for the maintainers to decide.
Verdict: REQUEST_CHANGES
Reasoning: The delete blocker is fixed, but the Build size budget and Comments checks fail because of this change, and a cancelled Studio render can still leave the sidecar behind.
Both Studio adapters cleaned up a cancelled render by removing only the output and its .meta.json. The sidecar is written while the output is hashed, so a cancel during that step left it behind. The adapters now remove every path from renderArtifactPaths, built from the same extension list the delete route uses. Also drops the comment above RENDER_SIDECAR_EXTENSIONS and raises the producer size budget to this branch's measured size (784260 packed, 2855776 unpacked): the sidecar module ships in both the index.js and public-server.js bundles.
miguel-heygen
left a comment
There was a problem hiding this comment.
Thanks, all three points are fixed, and the cancel fix went to the right place. renderArtifactPaths builds from the same extension list the delete route uses, so delete and both cancel paths now agree on which files a render leaves behind, and the new test pins the sidecar in that list. The budget bump is explained in the description, and Build and Comments are green.
One check is still red, and it only needs a description edit: Studio and player captures fails because the branch now touches packages/studio/vite.adapter.ts. That change is 6 lines with nothing visible, so adding a ## No visible change section to the PR description should satisfy it (the check allows that for changes under 20 lines with no .tsx, .css or .html).
The "Studio: edit accuracy gate" run is the same fork limitation as before, not this branch.
On default-on versus opt-in, that's still for the maintainers to decide. From the code side this looks good to me once the captures check passes.
Verdict: COMMENT
Reasoning: Every earlier finding is fixed and the code checks are green, but the Studio captures check is red until the description gets a "No visible change" section, and default-on still needs a maintainer decision.
|
All three are fixed in 2ee6626, which also merges current
Because this now touches |
|
The provenance file is useful, but we don't want a new file next to every render by default. Can you make it opt-in (a flag or config key) and drop the size budget bump if it's no longer needed? Happy to merge after that. |
|
@jrusso1020 f793231 merges current On the budget: I took main's numbers, so this PR no longer touches Locally on f793231: studio-server route tests (688), the CLI render and server tests, the producer provenance tests, oxfmt, oxlint and Making the sidecar opt-in is the next commit. |
…ckage The sidecar schema in packages/core/schemas put @hyperframes/core's unpacked size over its budget. Nothing imports it from core, and its $id points at hyperframes.heygen.com/schema/, which Mintlify serves from docs/schema. Move it there, like docs/schema/hyperframes.json, so the $id URL resolves and core ships the same files as main.
|
Build was red on f793231 because of this branch: the sidecar schema in b14e15c moves the schema to The producer provenance tests (20) and oxfmt and oxlint on the changed files pass locally. The other red checks on f793231 were the Windows engine-cli job hitting its 16 minute deadline (main has since split it into shards), the regression shard timing out while building its Docker image, and the edit accuracy gate that skips on forks. Making the sidecar opt-in is still the next commit. |
The sidecar and the output hash now only happen when asked for. hyperframes render takes --provenance to write <output>.hf-render.json and --provenance-path <path> to write it elsewhere. Render requests default to no sidecar; provenance: true or a path turns it on.
|
Pushed bf8120e. The provenance sidecar is now opt-in: no file and no output hash unless |
…nce-sidecar Conflicts: docs/docs.json Main moved the Developers navigation under the Docs tab. Kept main's structure and added reference/render-provenance to the same group.
|
cc13373 merges current On the merge, Some local results were not clean:
I did not run the full CLI suite on this merge, only the render, server and docker args tests. |
What
Every successful render now writes a portable JSON receipt next to the output artifact:
out.mp4getsout.mp4.hf-render.json. The sidecar records tool versions (producer / node / ffmpeg), the entry HTML sha256, the compiled-composition hash,@font-facefamilies, a variables hash (count + sha256 of canonical JSON, never the raw values), format / exact-rational fps / resolution / duration / total frames, encoder facts (codec, preset, pixel format), the committed artifact's sha256 + size, per-stage wall-clock timings, worker count, HDR flag, outcome, and capture-readiness warning codes.--no-provenance(or--provenance false) disables;--provenance <path>relocates the sidecar.<row output>.hf-render.json(verified: distinct variables hashes per row). A fixed custom path with--batchis rejected up front.--dockeris rejected up front (not container-visible).packages/core/schemas/hf-render-sidecar.json($idhttps://hyperframes.heygen.com/schema/hf-render-sidecar.json); every emitted sidecar in testing validates against it (ajv, draft 2020-12).docs/reference/render-provenance.mdx, registered under the "Rendering paths" nav group.Why
Agents and CI need a machine-readable receipt beside the MP4 answering "what rendered this, from what inputs, and how" without re-running anything. Reproducibility checks, cache keys, and support triage all want it. Today the closest thing (
runtimeEnvSnapshot,perfSummary) is internal-only, and the engine's embedded container tags carry only renderer name + version. The sidecar complements those tags: full receipt, separate file, deliberately excluding secrets (variable values are hashed, no env dump, no host paths/usernames).How
packages/producer/src/services/render/provenanceSidecar.ts: sidecar builder + emitter. Wired intorenderOrchestrator.tsimmediately afterartifactTransaction.commit()(the actual commit site; there is nostages/commitArtifact.tsin the codebase). A sidecar write failure logs a warning and never un-completes a render whose artifact already committed.Reuses existing producer facts at the commit site: the compiled HTML (fonts scan), the observability composition hash, the encoder preset,
perfStages,workerCount, and job warnings. Producer version prefers the bundler-injected__PRODUCER_VERSION__define (the CLI inlines the producer vianoExternal, where the package-walking reader can't resolve) with the filesystem walker as the unbundled fallback; ffmpeg version is probed once via the engine's binary resolution (honorsHYPERFRAMES_FFMPEG_PATH) and cached.Threading:
--provenanceflag → render plan (parseProvenanceArg, tri-state) →RenderOptions→createRenderRequest(validated:falseor non-empty string) →RenderConfig.provenance→ orchestrator.dockerRunArgsforwards--no-provenance.png-sequencedirectory outputs skip artifact sha256/size;encoderisnullforpng-sequence/gif.packages/studio-server/src/routes/render.ts: the Studio delete route and both Studio cancel cleanups remove<jobId>.<ext>.hf-render.jsonalong with the output and.meta.json, from one list (renderArtifactPaths).Package size:
scripts/package-size-budgets.jsonraises the@hyperframes/producerbudget to 784,260 bytes packed and 2,855,776 unpacked, this branch's measured size after merging main. The sidecar module ships in both theindex.jsandpublic-server.jsbundles, which puts the package more than 1% over the previous budget.Test plan
provenanceSidecar.test.ts(19 tests: path resolution incl. directory outputs, font extraction, canonical variables hashing, receipt shape, secrets-not-embedded, emit integration incl. write-failure and unreadable-entry tolerance) andrender.provenance.test.ts(12 tests: flag parsing, plan threading, batch/docker conflicts, docker arg forwarding). Producer unit lane: green exceptaudioPadTrim.integration.test.ts, which fails identically on unmodifiedmainin this environment (host-ffmpeg loudness variance, pre-existing and unrelated).output.sha256matchessha256sumof the MP4),--no-provenance(no sidecar),--provenance <path>(relocated),--batchwith 2 rows (one sidecar per row, distinct variables hashes), sidecars validated against the JSON Schema with ajv.bun run build,tsc --noEmit(producer + cli), oxlint/oxfmt, and the full lefthook pre-commit suite (incl. the fallow gate) all pass.docs/reference/render-provenance.mdx+docs/docs.json.Walkthrough (HyperFrames-rendered)
The demo was storyboarded first (script, beat sheet, and seam vector ledger:
render-provenance-ce-plan.md), built as a HyperFrames composition (source) that passedhyperframes lintandhyperframes check(0 errors, WCAG AA contrast), and rendered with:Self-proof: that render exercised this PR's feature on itself. Its own receipt,
render-provenance-walkthrough.mp4.hf-render.json, recordswhich is exactly
sha256sumof the rendered MP4.Rebased on
main(bb9e507). Since main now has HLS output, the sidecar treats HLS as a directory artifact (no size or sha256) and the schema listshlsas a format. After the rebase: clitsc, producertsc, lint, oxfmt,sync-schemas:checkand the comment checks pass; the provenance/render/docker tests (188) and producer sidecar/renderRequest tests (39) pass.This replaces #3758, which could not be reopened.
No visible change
The only Studio change is in
packages/studio/vite.adapter.ts: cancelling a render now also removes its.hf-render.jsonsidecar fromrenders/. The render list only shows video files, so nothing on screen changes.