--- id: doc-8 title: "Opus 4.7, xhigh analysis" type: other created_date: "2026-05-04 15:08" updated_date: "2026-05-04 15:10" --- # Nerves Deployment Research — Findings Three parallel research agents covered NIFs, SQLite extensions, and Litestream replication semantics. The user's concerns were well-founded: there are real blockers, but most are solvable with custom Nerves system work. The Litestream question reframes the architecture more than expected. ## 1. Native libraries (NIFs on aarch64-linux-musl) | Library | aarch64-gnu | aarch64-musl | Status | | -------------------------- | ----------- | ------------ | ---------------------------------------------------------------------------------- | | `mdex` | ✅ | ✅ | Fine | | `lumis` | ✅ | ✅ | Fine | | `typst` | ✅ | ✅ | **Nerves-aware** — has `@nerves_rust_target_triple_mapping` in mix.exs | | `ecto_sqlite3` / `exqlite` | ✅ | ✅ | Driver fine; designed with embedded use in mind. Extensions are the issue (see §2) | | `dominant_colors` | likely | unknown | Verify GitHub releases; if missing, request musl target from maintainer | | `vix` | partial | ❌ | **Major blocker** | | `cloak_ecto` | n/a | n/a | Pure Elixir + `:crypto` | **`vix` is the dominant blocker.** Two compounding issues: - It loads the NIF at _compile time_ (macro-driven introspection), which breaks cross-compilation on stock Nerves systems. fhunleth (Nerves core) discussed this in a 2023 ElixirForum thread; the conclusion was "no easy Elixir-level fix." - libvips itself is not in `nerves_system_rpi4` / `nerves_system_rpi5`. You'd need a **custom Nerves system** with `BR2_PACKAGE_LIBVIPS=y` in Buildroot, then `VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS`. This is doable (15–30 min Buildroot rebuild, well-documented) but it commits you to maintaining a custom Nerves system. That single decision affects everything downstream — once you're building custom systems, ICU and sqlite-vec become "while we're at it" additions. **General Phoenix-on-Nerves status (2025):** Pattern is established but niche. Underjord's "LiveView on Nerves" guide is the canonical reference; a poncho project (sibling `ui` + `firmware`) is preferred over umbrella. No widely-cited "production Phoenix LiveView on Nerves at non-hobby scale" guide exists — most published examples are thermostats and birdhouses. ## 2. SQLite extensions Both extensions are blockers on stock Nerves systems for the same root reason — Nerves uses musl, prebuilt binaries assume glibc. **`sqlite-vec` (`vec0`):** - Upstream publishes `linux-aarch64` but it's **glibc** — won't `dlopen` on Nerves. - No musl prebuilt exists; PR #199 fixes the musl compile but is **unmerged**. - The `sqlite_vec` Hex wrapper (Joel Paul Koch's) downloads the glibc binary and would fail at runtime. - Mitigation: cross-compile inside Buildroot, ship via `rootfs_overlay/`. Or compile statically into exqlite at firmware build time (recommended pattern from the SQLCipher-on-Nerves ElixirForum thread). **`unicode` (ICU) extension:** - ICU is ~30 MB and deliberately excluded from Nerves base systems. - Requires a custom Nerves system with `BR2_PACKAGE_ICU=y`. - If you're already building custom for `vix`, the marginal cost is minimal. **Good news for `exqlite`:** Driver itself ships precompiled musl NIFs (since v0.13.7), supports `load_extension`, and has been used on Nerves in production (the SQLCipher thread). The complexity is purely in shipping the extension `.so`s built against the same musl toolchain. ## 3. Litestream replication — this is the architectural pivot **Headline:** Litestream is _not_ a multi-master replicator. It's a one-way backup tool. If the Nerves device writes locally, those writes are lost on the next restore — no conflict detection, no reconciliation, no merge. **Two viable replica modes today (both newer than the docs you may remember):** 1. **VFS read replica** (stable, in `litestream.io/guides/vfs/`): Nerves device queries S3-backed replica directly. ~1 minute staleness matches your sync interval. Writes return `"litestream is a read only vfs"` at the SQLite layer. **Requires CGO** — verify this works in your Nerves build. 2. **Restore-then-poll**: simpler, no CGO. Restore on startup, periodically re-restore. Live replication via the embedded HTTP server existed but the example repo was **archived November 2023** — treat as experimental. **For the architectures you'd consider:** - **Read-only Nerves device** (browse music library at home, no edits) — Litestream VFS replica works, modulo CGO. This is the simplest path. - **Read-write Nerves device** (record listening, edit notes) — Litestream alone is insufficient. Three options: 1. **Split databases**: music library is read-only via Litestream; a separate small SQLite on the device captures writes and pushes to the production API out-of-band. Cleanest fit for your existing app shape, since scrobbles are already a separate domain. 2. **LiteFS** (Fly.io's project): true primary/replica with write-forwarding. Requires FUSE — not viable on Nerves without kernel module support, so likely off the table. 3. **Writes go directly to production API**: Nerves device calls the prod Phoenix JSON API for any write; reads are local. Requires the device to be online for writes (acceptable for a home device). - **Other distributed-SQLite options** — rqlite (Raft, kills write throughput), Marmot (NATS-based CDC, eventual consistency), cr-sqlite (CRDTs). All exist; all are heavier than the problem warrants for one home device. ## Synthesis: where the unknowns concentrate The three concerns aren't independent — they collapse into **one decision**: are you willing to maintain a custom Nerves system? - If **yes**: `vix`, ICU, and `sqlite-vec` are all solvable in the same Buildroot config. The remaining unknowns are minor (`dominant_colors` musl target, exact poncho structure for the existing umbrella-style codebase). - If **no**: the application as currently architected cannot run on Nerves. You'd have to drop image processing, drop FTS unicode collation, drop similarity search — at which point it's a different application. The **biggest non-obvious unknown** is the Litestream architecture decision. Going read-only on the Nerves side is dramatically simpler than supporting writes, and it covers most of the value (browsing, search, AI chat against the local collection). Splitting writes to a separate small DB that pushes to prod is the natural compromise — and your codebase is already structured to support it (scrobbles are a separate context, notes are a separate schema). **Suggested next step if you want to proceed:** prototype a custom `nerves_system_rpi5` with libvips + ICU on a feature branch, before touching the application. That single artifact resolves three of the four major risks; everything else is application-layer work that can be done iteratively. --- ## Sources ### NIF / Phoenix-on-Nerves - [Underjord: LiveView on Nerves](https://underjord.io/liveview-on-nerves.html) - [ElixirForum: Cross Compiling with NIF as compilation dependency (vix/Nerves thread)](https://elixirforum.com/t/cross-compiling-with-nif-as-compilation-dependency/59821) - [Nerves docs: Compiling Non-BEAM Code](https://hexdocs.pm/nerves/compiling-non-beam-code.html) - [rustler_precompiled Precompilation Guide](https://hexdocs.pm/rustler_precompiled/precompilation_guide.html) - [mdex GitHub releases (NIF targets)](https://github.com/leandrocp/mdex/releases) - [lumis GitHub releases](https://github.com/leandrocp/lumis) - [typst Elixir GitHub releases](https://github.com/Hermanverschooten/typst/releases) - [vix HexDocs README](https://hexdocs.pm/vix/readme.html) - [exqlite README](https://hexdocs.pm/exqlite/readme.html) - [NervesConf 2024 Talks thread](https://elixirforum.com/t/nervesconf-2024-talks/75205) - [Customizing Your Nerves System](https://hexdocs.pm/nerves/customizing-systems.html) ### SQLite extensions - [Releases · asg017/sqlite-vec](https://github.com/asg017/sqlite-vec/releases) - [Fix for musl compile PR #199 · asg017/sqlite-vec](https://github.com/asg017/sqlite-vec/pull/199) - [Build and install exqlite with sqlcipher into nerves rpi5 image — ElixirForum](https://elixirforum.com/t/build-and-install-exqlite-with-sqlcipher-into-nerves-rpi5-image/75089) - [Concerning warning compiling under Nerves · Issue #166 · elixir-sqlite/exqlite](https://github.com/elixir-sqlite/exqlite/issues/166) - [Compiling sqlite-vec — Alex Garcia](https://alexgarcia.xyz/sqlite-vec/compiling.html) - [GitHub — joelpaulkoch/sqlite_vec](https://github.com/joelpaulkoch/sqlite_vec) - [Advanced Configuration — Nerves](https://hexdocs.pm/nerves/advanced-configuration.html) ### Litestream - [Live Read Replication — Litestream (tip)](https://tip.litestream.io/guides/read-replica/) - [VFS Read Replicas — Litestream](https://litestream.io/guides/vfs/) - [How it works — Litestream](https://litestream.io/how-it-works/) - [Tips & Caveats — Litestream](https://litestream.io/tips/) - [Alternatives — Litestream](https://litestream.io/alternatives/) - [Litestream: Revamped — Fly.io Blog](https://fly.io/blog/litestream-revamped/) - [benbjohnson/litestream-read-replica-example (archived 2023)](https://github.com/benbjohnson/litestream-read-replica-example) - [Live read replicas — Issue #8](https://github.com/benbjohnson/litestream/issues/8) - [LiteFS vs Litestream vs rqlite vs dqlite on VPS in 2025](https://onidel.com/blog/sqlite-replication-vps-2025) - [Marmot distributed SQLite replicator](https://github.com/maxpert/marmot) - [mvSQLite](https://github.com/losfair/mvsqlite) - [cr-sqlite](https://github.com/vlcn-io/cr-sqlite)