Declarative Android apps and reachable device state via
nix-android — nix-darwin, but
for your phone. Converge over authorized adb shell; no root, no replacing
the OS, no Nix on the device.
| Piece | Path / attr |
|---|---|
| Device module | hosts/android/oneplus6t/ |
| APK lock | hosts/android/oneplus6t/apps.lock.json |
| Flake wiring | modules/nix-android.nix |
| Config (Apple Silicon controller) | .#oneplus6t-darwin |
| Config (x86_64 Linux controller) | .#oneplus6t-linux |
| CLI | nix run .#android-rebuild -- … |
| Wireless adb | nix run .#adb-wireless -- … / scripts/adb-wireless.sh |
| Always-on agent | dendritic.androidConverge (mba launchd + sliceanddice systemd) |
Shared Home Manager module modules/apps/android-converge.nix
installs the same agent on mba (launchd) and sliceanddice (systemd user
timer). Default interval is 15 minutes; RunAtLoad / OnBootSec so it is
always on when the host is up.
No duplicate apply: before switch, the agent writes a short lease on the
phone (/data/local/tmp/nix-android-<device>.lease) with hostId + expiry.
If the other controller holds a valid lease, this host skips. A local
~/.cache/android-converge.lock also prevents overlapping runs on one machine.
# Logs
tail -f ~/.cache/android-converge.log # mba
journalctl --user -u android-converge -f # sliceanddice
# Manual kick
launchctl kickstart -k gui/$(id -u)/com.aspaulding.android-converge # mba
systemctl --user start android-converge.service # sliceanddiceEnabled with dendritic.androidConverge.enable = true on both controllers
(hostId defaults from dendritic.fleet.hostId).
Upstream’s first-class targets are stock Pixel and GrapheneOS. Other
AOSP-derived phones (including LineageOS on OnePlus 6T) are best-effort:
successful adb authorization alone is not a support claim. Review every
plan before switch or bootstrap. See upstream
SUPPORT.md
and LIMITS.md.
Controllers: Apple Silicon macOS (mba) and x86_64 Linux
(sliceanddice). Both already have android-tools / adb.
-
USB debugging enabled on the phone (or Wireless debugging — see below); accept the RSA prompt once.
-
Confirm the serial (
USBhardware id, or wirelessIP:PORT):adb devices -l # or nix run .#adb-wireless -- status
-
Declared ABI is
arm64-v8a(OnePlus 6T). The engine refuses a mismatch withro.product.cpu.abi.
nix-android only needs a working adb serial — wireless works the same as USB. Two setup paths:
Same LAN as the controller. Plug in once:
nix run .#adb-wireless -- tcpip # enables adb tcpip 5555, connects
# unplug USB
nix run .#adb-wireless -- statusLater reconnects (phone IP may change):
nix run .#adb-wireless -- connect 192.168.1.40:5555On the phone: Settings → System → Developer options → Wireless debugging → enable → Pair device with pairing code. Then:
nix run .#adb-wireless -- pair 192.168.1.40:37123 123456
nix run .#adb-wireless -- connect 192.168.1.40:41259 # IP & port from Wireless debuggingLineageOS exposes this when based on Android 11+. Older builds use path A.
SERIAL=$(nix run .#adb-wireless -- serial)
nix run .#android-rebuild -- plan --flake .#oneplus6t-darwin --serial "$SERIAL"adb-wireless remembers the last HOST:PORT under
~/.local/state/adb-wireless/last-endpoint so bare connect works next time.
From the repo root. On mba use oneplus6t-darwin; on sliceanddice use
oneplus6t-linux.
# After editing apps.* in hosts/android/oneplus6t/default.nix — refresh pins
nix run .#android-rebuild -- update --flake .#oneplus6t-darwin \
--lock hosts/android/oneplus6t/apps.lock.json
git add hosts/android/oneplus6t/apps.lock.json
# Read-only plan (may fetch/build APKs on the controller)
nix run .#android-rebuild -- plan --flake .#oneplus6t-darwin --serial SERIAL
# Apply only after reviewing the plan
nix run .#android-rebuild -- switch --flake .#oneplus6t-darwin --serial SERIAL
# Drift since last successful switch
nix run .#android-rebuild -- status --flake .#oneplus6t-darwin --serial SERIALPlay / attended apps need human consent:
nix run .#android-rebuild -- assist --watch --flake .#oneplus6t-darwin --serial SERIALWiped-device resumable path (review plan first):
nix run .#android-rebuild -- bootstrap --flake .#oneplus6t-darwin --serial SERIALTo draft a fuller config from what is already installed:
nix run .#android-rebuild -- import --serial SERIALPaste the generated module into hosts/android/oneplus6t/default.nix, then
update → plan → switch.
- Cleanup on (
apps.cleanup = "uninstall") — undeclared third-party owner apps are removed after a successful apply. System packages are never cleanup candidates; disable those withandroid.packages.disabled. - Keepers today: F-Droid + Termux (managed) and Wawona
(
com.aspauldingcode.wawonaunderapps.attended). Add anything else you want to keep underapps.play/apps.attended/apps.fdroid/apps.releasebefore the nextswitch, or it will be uninstalled. - Dark mode + opportunistic Private DNS; Termux notification permission and background/battery exemptions.
Full option surface: upstream USING.md and OPTIONS.md.