Nuvaka › Developer docs › Program packaging
Program packaging: platform keys, files and services
Your program ships as one package per platform. This page lists the 10 platform keys, the file name rules, the server's file-type check and how Nuvaka General App installs each package. It also shows how to install a system service without asking the user to run terminal commands. For the full publishing flow see Programs.
Platform keys
| Key | File | File-type check (first bytes of the file) | How the app installs it |
|---|---|---|---|
windows-msi | .msi | OLE: D0 CF 11 E0 A1 B1 1A E1 | msiexec /i; the Windows Installer window opens |
windows-exe | .exe (x86_64) | MZ | The setup program starts |
windows-exe-arm64 | .exe (arm64) | MZ | The setup program starts |
windows-msix | .msix | ZIP: PK 03 04 | App Installer opens |
macos-dmg | .dmg | koly in the last 512 bytes | The disk image opens; the user drags the app to Applications |
linux-appimage | .AppImage | ELF: 7F 45 4C 46 | Copied to ~/Applications, made executable and started; no admin rights |
linux-deb | .deb | !<arch>\n | apt-get install after a permission prompt (polkit) |
linux-rpm | .rpm | ED AB EE DB | dnf install (or zypper install without dnf) after a permission prompt |
linux-pacman | .pkg.tar.zst | zstd: 28 B5 2F FD | pacman -U after a permission prompt |
android-apk | .apk | ZIP: PK 03 04 | Android package install |
- The file-type check is light: it only reads the start of the file (the end for dmg). A mismatching file is rejected with
400 bad_package. The review team checks the package contents. - The store list and the update check accept either the full key (
linux-deb) or the OS (linux) as a filter. - One version can have several keys, e.g.
windows-msi+windows-exe-arm64+linux-deb+linux-rpm+linux-appimage. The app picks the best fit for the device (matching).
File names
- A file name starts with a letter or digit. It contains only letters, digits,
._+-and spaces, and is at most 151 characters long. - It must end with the key's extension (case-insensitive), otherwise you get
400 bad_file_name. For pacman the extension is.pkg.tar.zst;.tar.zstis not accepted. - Suggested names, following each ecosystem's habits:
App-1.2.0-x64.msi,App-1.2.0-Setup.exe,App-1.2.0-arm64-Setup.exe,App-1.2.0.msixApp-1.2.0.dmg,App-1.2.0-x86_64.AppImageapp_1.2.0_amd64.deb,app-1.2.0-1.x86_64.rpm,app-1.2.0-1-x86_64.pkg.tar.zstapp-1.2.0.apk
- The version inside the package should match the version in Nuvaka. The update check uses the Nuvaka version; if the package's own version differs, the OS may treat the update as a downgrade and refuse it.
Platform notes
Windows
- MSI (WiX, Advanced Installer…): use the same
UpgradeCodein every version and define aMajorUpgradeso updates work. A per-machine install (ALLUSERS=1,InstallScope="perMachine") brings up the UAC prompt by itself. - EXE (NSIS, Inno Setup…):
windows-exeis for x86_64 andwindows-exe-arm64for arm64. If one installer covers both architectures, upload it aswindows-exe; Windows 11 on ARM runs it under emulation. - MSIX: an unsigned MSIX cannot be installed. The signing certificate must be trusted on the user's device.
- Authenticode signing is recommended for installers. Unsigned installers trigger SmartScreen warnings.
macOS
- Put a signed and notarized
.appin the DMG together with a shortcut to the Applications folder. A single universal build (lipo) for Apple Silicon and Intel is simplest; declare"arch":"universal"in compat.
Linux
- deb, rpm and pacman are installed with the distribution's package manager. Declare system libraries in the package's own dependency field (
Depends:,Requires:,depends=()); the package manager installs them from the repositories. The Nuvaka dependencies list is for things the package manager cannot install: drivers, hardware, other Nuvaka programs. - rpm packages do not need a GPG signature. The app re-checks the file's verified hash on the root side and disables the GPG check only for that local file.
- AppImage needs no root and runs on any distribution, provided its glibc is at least the version you declare in compat. An AppImage cannot install a system service. If you need one, publish a deb, rpm or pacman package.
Android
- A universal APK (all ABIs) is recommended; declare
"arch":"universal"in compat. Updates must use the same package name and signing key.
Installing system services
Some programs need a background service, e.g. a hardware daemon or a network agent. The ground rule:
An install never asks the user to open a terminal, type a sudo command, copy files or start a service by hand. The package itself installs, enables and starts the service, and stops and removes it on uninstall. The app installs Linux packages through polkit (pkexec), so the user sees a single admin password prompt. Package scripts must not ask questions and must run non-interactively.
deb: postinst + systemd
Ship the unit as /lib/systemd/system/app.service. If you use debhelper, dh_installsystemd generates the scripts below for you. By hand:
# debian/postinst
#!/bin/sh
set -e
if [ "$1" = configure ]; then
systemctl daemon-reload
systemctl enable app.service
systemctl restart app.service
fi
# debian/prerm
#!/bin/sh
set -e
if [ "$1" = remove ]; then
systemctl disable --now app.service || true
fi
# debian/postrm
#!/bin/sh
set -e
[ "$1" = purge ] && rm -rf /var/lib/app
systemctl daemon-reload || true
rpm: %post and the systemd macros
# app.spec
%{?systemd_requires}
BuildRequires: systemd-rpm-macros
%files
%{_unitdir}/app.service
%post
%systemd_post app.service
# Fedora presets may keep the service disabled; if your program does not work without it:
systemctl enable --now app.service >/dev/null 2>&1 || :
%preun
%systemd_preun app.service
%postun
%systemd_postun_with_restart app.service
pacman: the .install file
# PKGBUILD
install=app.install
package() {
install -Dm644 app.service "$pkgdir/usr/lib/systemd/system/app.service"
}
# app.install
post_install() {
systemctl daemon-reload
systemctl enable --now app.service
}
post_upgrade() {
systemctl daemon-reload
systemctl try-restart app.service
}
pre_remove() {
systemctl disable --now app.service
}
Windows: an admin install with NSIS
If the installer requests admin rights, Windows shows the UAC prompt itself. The installer registers the service and the uninstaller removes it:
; app.nsi
RequestExecutionLevel admin
InstallDir "$PROGRAMFILES64\App"
Section "Install"
SetOutPath "$INSTDIR"
File "app-service.exe"
nsExec::ExecToLog 'sc.exe create AppSvc binPath= "\"$INSTDIR\app-service.exe\"" start= auto DisplayName= "App"'
nsExec::ExecToLog 'sc.exe start AppSvc'
WriteUninstaller "$INSTDIR\uninstall.exe"
SectionEnd
Section "Uninstall"
nsExec::ExecToLog 'sc.exe stop AppSvc'
nsExec::ExecToLog 'sc.exe delete AppSvc'
RMDir /r "$INSTDIR"
SectionEnd
- In Inno Setup, use
PrivilegesRequired=adminwith the[Run]and[UninstallRun]sections. In WiX/MSI, use theServiceInstallandServiceControlelements. - Install programs that need no service per user (without admin rights). Then no UAC prompt appears at all.
macOS
- Register background work from inside the app with
SMAppService(macOS 13+):SMAppService.agentfor a per-user agent,SMAppService.daemonfor a system daemon. The user approves a system daemon in System Settings.
Nuvaka Apps API v1 · last updated 2026-10-17