# Contributing Thanks for helping. Bug reports from real phones are especially valuable; include the phone model, Android version, and the service's log (dashboard → Logs, or `files/logs/` over SFTP). ## Development setup 1. Install JDK 21+, Go, the Android SDK and NDK; set `ANDROID_NDK_HOME`. 2. `native/build-all.sh` builds the bundled binaries into `app/src/main/jniLibs/` (ignored by git). 3. `./gradlew assembleDebug` and install the APK for your phone or emulator's ABI. An x86_64 emulator with KVM is the fastest way to iterate; `adb forward tcp:8800 tcp:8800` makes the dashboard reachable on your computer. Emulator MediaCodec is a software codec, so hardware-transcoding speed has to be checked on a real phone. On a Windows checkout with project-local tools installed, run: ```powershell . .\tools\dev.ps1 .\gradlew.bat assembleDebug testDebugUnitTest lintDebug node tests/ipcam-dashboard.test.cjs ``` The tools live in the ignored `.tools` folder (JDK 21, SDK platform/build-tools 36, NDK 30.0.16248370, platform-tools and Go). `tools/dev.ps1` selects them for the current shell. `tools/build-go-native.ps1` builds SSH, Caddy, Cloudflare and Tailscale on Windows with the Windows NDK. It applies the same security dependency pins as `native/build.sh`. The proot and FFmpeg build scripts require Linux, the Linux NDK and standard compiler tools; run those in WSL or Linux. For Kotlin/dashboard-only changes, unchanged native libraries can be extracted from a checksum-verified matching project release into `app/src/main/jniLibs`. Do not use cached release binaries to validate changes to native sources. Local debug APKs use a local debug signing key. Updating an installed release while preserving its data requires its original signing key; restore `keystore.properties` and that keystore from the signing computer before building a release update. Do not uninstall the existing app to work around a signing mismatch. For camera hardware verification on a USB-connected ARM64 phone with USB debugging enabled, run `./tools/camera-hardware-test.ps1`. It installs a separate `dev.homedroid.cameratest` package, tests dashboard authentication, live frames, rotation, bounded playable motion clips and each exposed camera, then removes only those test packages. It never replaces the release app. Rebuild with `./gradlew.bat assembleDebug` afterward to produce the normal application ID. ## Code layout | Path | What | |------|------| | `app/src/main/java/dev/homedroid/` | The app: service, supervisor, dashboard, Alpine/proot, image puller | | `app/src/main/assets/apps.json` | The app catalog | | `app/src/main/assets/dashboard.html` | The web dashboard (single file, no build step) | | `native/` | Build scripts for bundled binaries, the SSH server, and upstream patches | The app has no third-party dependencies beyond the Kotlin standard library, and that's deliberate: keep it light. ## Adding an app to the catalog A single-process app runs in Alpine: ```json { "id": "myapp", "name": "My App", "description": "…", "port": 1234, "size": "≈ 50 MB", "install": "apk add --no-cache myapp", "uninstall": "apk del myapp", "run": ["/usr/bin/myapp", "--port", "1234"], "env": {}, "storage": {"path": "/var/lib/myapp/files", "label": "Files"} } ``` A multi-process app can run container images (see `immich` in `apps.json`). Use `images`, `services` and `arches`; `binds` take `@data/` (kept in the app's data folder) or `@storage` (the user's chosen location), and env values may use `{{secret}}`. Things that don't work in an Android app sandbox, and how the catalog works around them: - **No hard links.** proot fakes them, but tools that rely on them should be told not to (e.g. `UV_LINK_MODE=copy`, `git config core.createObject rename`). - **No System V IPC.** Services that need it (PostgreSQL) set `"sysvipc": true`. - **No `/dev/ashmem`, no raw USB or Bluetooth devices, no ports below 1024.** - **Some `/proc` files are hidden.** Homedroid provides stand-ins for `stat`, `loadavg`, `uptime`, `version` and `vmstat`. ## Pull requests Keep changes focused, match the surrounding style, and describe how you tested (device, Android version). Patches to upstream projects go in `native/patches/` as unified diffs, with a comment explaining why.