Native desktop execution
Bind declared native actions to typed inputs, trusted execution and retained output receipts.
Native desktop execution
A Block declaring manifest.desktopTools, permissions.desktop: ["native.tools"], or a package-level desktop mapping requires a trusted desktop executor. Its JavaScript or recipe cannot establish that a native application ran. The generic JavaScript worker and synchronous recipe runner reject these packages with DESKTOP_EXECUTION_REQUIRED.
This guard applies conservatively to existing native packages too. Their immutable source, release identity and installed pins are unchanged. A legacy package without the mapping below can retain its reviewed interactive tool controls, but generic connected execution requires a new mapped release. Do not rewrite old package bytes or treat return {video: input.video} as a render.
Declarative input and output mapping
The package's optional desktop object is stored as src/desktop.json in an editable SDK folder. The package must remain a stateless JavaScript or recipe package with its original sandbox rules, an approved native.tools declaration and desktop-only platforms. Version 1 currently supports only Natron adapter 1's project.render action:
{
"schemaVersion": 1,
"tool": "org.natron.Natron",
"action": "project.render",
"arguments": {
"projectId": {"input": "projectId"},
"input": {"input": "video"},
"reader": {"input": "reader"},
"writer": {"input": "writer"},
"firstFrame": {"input": "firstFrame"},
"lastFrame": {"input": "lastFrame"}
},
"outputs": {"video": "asset"}
}Each mapped port must be declared in the manifest. projectId, reader, and writer are text; video is a video reference; frame ports are integers. All six values must be present after declared defaults and input resolution. Map every declared output to a compatible approved result field; Natron's asset is a video. Constants, extra arguments, commands, executable paths, environment variables and scripts are not supported. New native actions require a reviewed host adapter and a corresponding SDK argument/result contract.
The same mapping survives JSON packages, portable archives, editable source downloads, folder/ZIP imports, CLI packaging and verified GitHub SDK-folder import. A mapping alone grants no permission and does not install or launch the application.
Trusted host execution
The custom interface calls StillMade.run with the connected input and render settings. The host supplies the executor to runPackageAsync; package code cannot install this callback:
await runPackageAsync(pkg, inputs, {
signal,
desktop: async (pinnedPackage, validatedInput, {signal, request}) => {
const invocation = desktopInvocation(pinnedPackage, validatedInput);
// Host-owned integration: check exact-source consent, project and media
// ownership, saved settings, approved operation, completion and artifact.
const completed = await authorizedNativeHostRun(invocation, {signal});
const outputs = desktopOutputs(pinnedPackage, completed.result);
return {
outputs,
desktopReceipt: {
schemaVersion: 1,
requestId: request.requestId,
packageDigest: request.packageDigest,
inputDigest: request.inputDigest,
outputDigest: await digest(outputs),
tool: pinnedPackage.desktop.tool,
adapterVersion: 1,
action: pinnedPackage.desktop.action,
executionId: completed.executionId
}
};
}
});authorizedNativeHostRun is illustrative host code, not an SDK API. It must perform the real approved native invocation, enforce cancellation and revocation, verify the output artifact and register its media identity before returning. It must not accept a receipt, output URL or claimed process success from Block code as authority. The Block cannot choose the executable, arbitrary path, credentials or permission scope.
desktopInvocation(pkg, inputs) returns the declared tool, operation (tool.project.render), bounded argument mapping and output mapping. desktopOutputs(pkg, nativeResult) extracts and type-checks the declared results. The SDK pins copied package bytes and validated inputs before calling the host, creates a fresh cryptographic UUID request identity, and checks receipt source/input/action/output bindings and cancellation before returning. RunPackageResult.desktopReceipt must be retained with outputs.
Receipt validation proves the envelope's bindings. It is not a cryptographic attestation of Natron, an independent check of stored media bytes, or permission to access another project's output. The trusted host still owns those checks. Host fixture mocks must never be described as native render proof.
Connected execution, reuse and admission
Native Steps require an interactive connected plan and executor. They form a runtime-approval boundary in ordinary local, independent or background plans. A connected native result without its receipt is rejected even when a caller-supplied generic executor returns correctly typed passthrough output. Retained successful results must keep the receipt bound to the exact package, actual validated inputs and output. validateDesktopExecutionRecord checks those bindings during resume/adoption without running the application; host ownership checks remain required. Changes to input, source or output invalidate reuse.
A reviewed condition or failure policy can still skip a Step. Such a record has skipped: true and no native receipt: it establishes a skip or fallback, not completed native work.
Offline validate, test, pack, admitLocalPackage and admitPackage without a desktop executor perform source and fixture-contract checks only. Reports carry execution: "not-run", tests: 0, fixtureContracts, liveVerified: false and reviewRequired: true. CLI preview and testPackage require the real executor to run; an unavailable executor never counts as a passing render. A supplied desktop executor can exercise contract fixtures, but SDK reports still do not claim independent live verification. Execute a representative authorized source through the real application, inspect the exported artifact, and verify downstream receipt and media identity before reporting integration complete.