Sandboxing
How Nucleus builds sandboxed distributions for the App Store, Flatpak, and MSIX store formats.
Three store formats require a sandboxed build: Pkg (macOS App Sandbox), Flatpak (Linux), and AppX (Windows MSIX). Sandboxed apps cannot extract native libraries to a temporary directory at runtime or load arbitrary code, so Nucleus runs a second build pipeline for these formats. This page describes what that pipeline does and how to configure the parts that need per-project values.
Request a store format
Add a store format to targetFormats. The sandboxed pipeline is enabled automatically when at least one store format is present.
nucleus {
application {
nativeDistributions {
targetFormats(
// Non-sandboxed pipeline
TargetFormat.Dmg,
TargetFormat.Nsis,
TargetFormat.Deb,
// Sandboxed pipeline
TargetFormat.Pkg,
TargetFormat.AppX,
TargetFormat.Flatpak,
)
}
}
}Run the standard packaging task. Both pipelines run in the same invocation:
./gradlew packageDistributionForCurrentOSHow it works
| Format | OS | Sandbox model |
|---|---|---|
Pkg | macOS | App Sandbox |
AppX | Windows | MSIX — full trust, but with packaging constraints |
Flatpak | Linux | Flatpak sandbox |
When at least one store format is configured, Nucleus registers additional Gradle tasks that:
- Scan dependency JARs for
.dylib,.jnilib,.so, and.dllfiles and extract them — intoContents/Frameworks/on macOS, and intoresources/on Windows and Linux. - Rewrite the JARs without those native entries to avoid duplicating them.
- Merge your
appResourceswith the extracted libraries. - Inject JVM arguments so
java.library.path,jna.library.path, andjna.boot.library.pathpoint at the signed extraction directory. JNA also receivesnounpack=trueandnosys=true. - Sign each native binary in
Contents/Frameworks/individually withcodesign(macOS). - Rewire Skiko and
icudtl.datto the same directory.
The non-sandboxed pipeline keeps running in parallel, so Dmg, Nsis, and Deb are built unchanged.
AOT cache and sandboxing
When generating an AOT cache for a sandboxed build, Nucleus strips jspawnhelper's code signature during the training phase — macOS terminates sandboxed forked processes otherwise — and re-applies it with the runtime entitlements before packaging.
Windows AppX
AppX runs at full trust and has the same system access as a regular Win32 app. It joins the sandboxed pipeline only because MSIX packaging forbids extracting DLLs to a temporary directory at runtime.
Configure macOS entitlements
Nucleus ships default entitlements for both sandboxed and non-sandboxed builds.
Non-sandboxed builds (Dmg, Zip):
<key>com.apple.security.cs.allow-jit</key> <true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/>
<key>com.apple.security.cs.disable-library-validation</key> <true/>Sandboxed builds (Pkg):
<key>com.apple.security.app-sandbox</key> <true/>
<key>com.apple.security.cs.allow-jit</key> <true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key> <true/>
<key>com.apple.security.cs.disable-library-validation</key> <true/>
<key>com.apple.security.network.client</key> <true/>
<key>com.apple.security.files.user-selected.read-write</key> <true/>The runtime entitlements applied to the bundled JVM binaries are stricter: no network and no file access. Only your app code declares capabilities. Override either set per build:
macOS {
entitlementsFile.set(project.file("packaging/entitlements.plist"))
runtimeEntitlementsFile.set(project.file("packaging/runtime-entitlements.plist"))
}Embed provisioning profiles
Mac App Store submission requires provisioning profiles embedded at Contents/embedded.provisionprofile:
macOS {
provisioningProfile.set(project.file("packaging/MyApp.provisionprofile"))
runtimeProvisioningProfile.set(project.file("packaging/MyApp_Runtime.provisionprofile"))
}Configure the Flatpak sandbox
Flatpak permissions are declared through finishArgs:
linux {
flatpak {
finishArgs = listOf(
"--share=ipc",
"--socket=wayland",
"--socket=pulseaudio",
"--device=dri",
"--filesystem=home",
"--share=network",
)
}
}See Linux targets for the remaining Flatpak options.
Tasks
These tasks are registered only when a store format is requested.
| Task | Purpose |
|---|---|
extractNativeLibsForSandboxing | Extract .dylib, .so, and .dll files from dependency JARs. |
stripNativeLibsFromJars | Rewrite the JARs without their native entries. |
prepareSandboxedAppResources | Merge appResources with the extracted libraries. |
createSandboxedDistributable | Build the app image with the sandbox JVM arguments. |
generateSandboxedAotCache | Train the AOT cache inside the sandbox. |
Notes
appStore is deprecated and ignored. Pkg is always treated as an App Store build: Nucleus applies the 3rd Party Mac Developer certificates and sandbox entitlements automatically.
- A single
./gradlew packageReleaseDistributionForCurrentOSbuilds both pipelines in CI. Sandboxed artifacts are written to<format>-sandboxed/subdirectories. - The sandboxed pipeline builds on the inside-out signing order used for universal macOS binaries. See Code signing.
What's next
- Code signing — sign and notarize macOS, Windows, and Linux builds.
- macOS targets — entitlements, provisioning profiles, and DMG layout.
- Linux targets — Flatpak, Snap, and AppImage configuration.