On-device AI for Android. No Google Play services, no telemetry, no cloud. Models, chats, RAG documents, and key material stay on the phone.
The point isn’t to clone ChatGPT into your pocket. It’s to give you a chat surface, a RAG pipeline, a voice loop, and a plugin runtime that all run with the radio off if you want them to.
Play Store · Repo · Releases · Issues · Discord
mmproj projector files. Image bytes cross AIDL as PFDs, never byte[].:server process. OpenAI-shaped endpoints, bearer-token auth, rate limit, audit log. Material 3 web UI bundled at /.Void2377/tool-neuron-plugins on HF. Each one is a sandboxed Android module with its own Compose UI, can ship ONNX models, and runs inside the host process behind a capability gate.Tool calling, Termux integration, anything cloud. The April 2026 scope pivot took those out and they’re not coming back. Image generation (:ai_sd) and the plugin marketplace came in instead, May 2026.
Three processes.
:app is the UI and where trust decisions live.:inference runs InferenceService over the GGUF engine and sherpa-onnx. Dies with the app.:server is foreground (dataSync, stopWithTask=false) so it survives swipe-from-recents and keeps the HTTP listener alive.Modules:
| Module | Purpose |
|---|---|
:app |
UI, viewmodels, Hilt graph |
:hxs |
Encrypted KV store with C++ core |
:hxs_encryptor |
Argon2id / AEAD / BoringSSL / ML-KEM-768 / ML-DSA-65 / Ed25519, plus the native policy + boot-integrity stack |
:native-server |
Embedded HTTP server (cpp-httplib + nlohmann/json) |
:download_manager |
Native downloader with JNI bridge |
:networking |
Network primitives in jniLibs |
:plugin-api |
Pure-Kotlin plugin contract — the only thing plugin authors compile against |
:plugin-exc |
Plugin runtime — DexClassLoader, capability gate, HF catalog client, dock |
:plugins:* |
First-party plugins (notes, counter, expense) — each is its own Android application module |
Trust decisions live in C++ so an obfuscation bypass at the Kotlin layer doesn’t grant access.
PolicyEngine. Every gated feature crosses JNI as PolicyEngine.isAllowed(Feature, sessionToken).<filesDir>/app_bootstrap/k.bin. The crypto is the wrapped ciphertext; the XOR is anti-grep.<filesDir>/app_prefs/ are sealed under HKDF(DEK, "tn.app_prefs.user_key.v1"). AuthState gets a second AEAD layer keyed on HKDF(DEK, "tn.app_prefs.auth_key.v1").1m → 5m → 15m → 1h → 4h → 12h → 24h. Tenth wipes device-side state. Clock rollback past five minutes is double-penalized.hardWipe() and returns VerifyResult.Wiped — indistinguishable from “attempts exceeded”..so in nativeLibraryDir, rebound to install identity ({signerHash, longVersionCode, lastUpdateTime}). Hook baseline verify catches inline hooks.strings libhxs_encryptor.so | grep -i frida returns nothing.FLAG_SECURE is on for PIN entry only. Chats stay screenshottable.CLAUDE.md is authoritative for the rest, including the planned pro-license hook (PolicyEngine.is_pro_feature(fid >= 1000)).
Found a vulnerability? Email siddheshsonar2377@gmail.com instead of opening a public issue.
Plugins live at Void2377/tool-neuron-plugins on HuggingFace. Each one is a zip — manifest.json + classes*.dex + optional lib/<abi>/*.so — under plugins/<id>/<version>/. The app reads plugins.json on every screen open. No local cache. The repo is the source of truth, every time.
Install flow: tap a plugin in the in-app store, runtime streams the zip into cacheDir, verifies SHA-256 against the manifest, extracts via PluginBundle, locks dex/so as read-only (Android 14+ rejects writable dex), deletes the temp file. The plugin’s classes load through DexClassLoader with plugin-api as the parent, and the host calls into Plugin.Content() — a @Composable that owns its own theme and scaffold.
Capabilities are declared in the manifest and gated by the host: hxs.read / hxs.write for storage, ai.onnx for ORT sessions, internet for network, plus camera, mic, filesystem, notifications, clipboard. If a plugin tries something it didn’t declare, the gate throws SecurityException.
To add to the public store:
./gradlew :plugins:<name>:packagePlugin
# drop the zip under plugins/<id>/<version>/, add an entry to plugins.json
hf upload Void2377/tool-neuron-plugins . .
# Dev loop
./gradlew :app:compileDebugKotlin
./gradlew :app:installDebug
# Release (R8 + resource shrink)
./gradlew :app:assembleRelease
Release signing reads from local.properties:
TN_KEYSTORE_PATH=/abs/path/to/keystore.jks
TN_KEYSTORE_PASSWORD=...
TN_KEY_ALIAS=...
TN_KEY_PASSWORD=...
Missing keys fall back to an unsigned release so the dev flow stays open. compileSdk 37, minSdk 31, ABI filters arm64-v8a + x86_64, JVM 17.
:hxs_encryptor fetches BoringSSL and liboqs via CMake FetchContent. :native-server fetches cpp-httplib v0.18.5 and nlohmann/json v3.11.3 the same way. The LSP sometimes flags missing openssl/mem.h; that’s a false positive — build-green is the source of truth.
SharedPreferences / Room / DataStore / raw files, with two intentional exceptions: the bootstrap DEK blob and content-addressed RAG source bytes.Scaffold. AppScaffold is the only one. Per-route top bars dispatch from AppTopBar.kt; bottom bars from AppBottomBar.kt. Screens take innerPadding: PaddingValues and render plain Column / LazyColumn / Box.:app minifies. Library modules collide on Type a.a is defined multiple times against pre-minified prebuilt jars (e.g. gguf_lib-release-runtime.jar) if they pre-minify too. Library rules go in each module’s consumer-rules.pro.Co-Authored-By trailer.CLAUDE.md at the repo root is project memory. Spec / plan / research / TODO docs don’t go in the tree.MIT.
.tar.bz2 voice archivesBuilt by Siddhesh Sonar. Come hang on Discord.