Execution boundaries and capability gates
Last reviewed: September 30, 2026.
Terminal, distro and lifecycle owner
The terminal renders VT output, accepts input and propagates geometry. A distro supplies packages, filesystem layout and session initialization. A VM owner additionally creates, starts, stops, persists and reconnects to a guest. These are separate contracts: reusing Termux-derived PRoot or PTY code does not require the Termux application, and using an Android Terminal-compatible disk image does not establish independent VM ownership. See the Sparkles baseline and AVF integration.
A local session needs a host PTY and child process. A VM session generally needs a guest PTY broker: the host transports bytes and explicit resize/control messages over a console or authenticated channel. A serial console alone does not offer independent tabs, working directories, signals and reconnectable sessions. Podroid's engine implements both console streams and guest control channels; its managed implementation supplies prior art, not proof of a no-DEX implementation.
Canonical store and ELF ABI
The logical store is the path embedded in derivations and outputs, normally /nix/store. The physical store may live under an app's private directory or /data/nix. Merely setting NIX_STORE_DIR or creating a symlink in app home cannot make absolute /nix/store/... references resolve globally. Nix's chroot store documentation requires namespaces for transparent relocation; choosing a different logical store sacrifices compatibility with the standard binary cache.
The ELF interpreter is the PT_INTERP path selected by the kernel before a dynamically linked executable reaches its entry point. Ordinary aarch64-linux Nix packages expect a glibc loader; Android applications use Bionic. Matching ARM64 instruction sets does not fix the loader path, libc ABI, dependency search paths or runtime syscall restrictions. PRoot supplies path translation, namespaces supply a real filesystem view, and a VM supplies a guest filesystem and kernel.
Run the ELF loader reader on a real executable:
dub docs/research/android-dev-env/concepts/examples/elf-loader.d /proc/self/exeThe program parses ELF headers and reports the actual interpreter; it does not infer compatibility from filenames. A static executable legitimately has no PT_INTERP.
Host page size
Android supports 16 KiB page-size configurations starting with Android 15. Check the actual value with getconf PAGESIZE, native ELF load-segment alignment and APK library alignment. Native libraries need compatible builds; a no-DEX APK is still a native application. Shared-kernel PRoot/NNS execution also uses the host page size, while a VM's guest kernel has its own memory-layout requirements. The Pad and reported Pixel 7 Pro both returned 4096 bytes; that does not establish the page size of every Pixel model/build or its future guests.
Namespaces, seccomp and SELinux
A user namespace can grant capabilities within a new namespace without granting capabilities over Android's parent namespace. A mount namespace gives a process its own mount tree. Kernel configuration, Android's inherited seccomp filter and SELinux rules are independent gates. A successful ADB shell probe is not evidence that a zygote-born application child can run the same syscall. NNS's kernel integration explicitly changes the exec path and app policy to open these gates.
The namespace probe calls unshare in a disposable child and confirms the parent mount namespace is unchanged. Missing capabilities print SKIP: on a normal Linux CI host. The Android app probe provides a Bionic-linked -betterC variant that can run in the custom terminal's actual session. Neither program mounts a filesystem or changes host policy.
The numeric result alone should not be overinterpreted: EINVAL can reflect unsupported namespace features or invalid combinations; EPERM can reflect capabilities or filtering. Read kernel configuration and policy where available before naming a unique cause. Observed device results retain the raw domain, UID and seccomp state.
AVF, hypervisor and protected firmware
AVF is Android's framework/service/VMM stack; pKVM, Gunyah and GenieZone are different hypervisor backends. Framework availability, management permission, custom-image permission and guest compatibility are separate checks. MediaTek's GenieZone crosvm adapter opens /dev/gzvm; it is not evidence that every MediaTek device exposes AVF to apps.
pvmfw runs before a protected guest and validates its boot inputs. It is not the hypervisor. Microdroid is a small Android guest for native payloads, not a synonym for an arbitrary Linux guest. A protected Linux guest may be possible, but an unsigned NixOS disk image cannot be presumed acceptable to a production verification chain. See Gunyah and AVF.
Persistence, networking and services
Persistence has three lifetimes: package/store files, session processes and the backend owner. App uninstall removes private files; Android can kill a process even while the display sleeps. A partial wake lock does not turn an activity into a foreground service. A VM disk can survive even when its owner and interactive shell disappear. Define reconnect, cancellation, snapshot/backup and startup behavior separately in the implementation milestones.
DNS is especially backend-specific. A static resolv.conf, Android's UID-aware resolver, a Bionic-to-glibc bridge and guest networking can behave differently under VPN or private DNS. Shared-kernel backends also inherit Android resource controls. Full NixOS service semantics require a guest kernel; portable runit services on Android do not provide equivalent systemd isolation.