How Sumire Implements Privacy-First Japanese Input With Zero Network Permissions
Sumire achieves privacy-first Japanese input by eliminating all network permissions from its Android manifest, running the open-source mozc conversion engine entirely offline, and encrypting all user learning data with AES-256-GCM via the Android Keystore.
The Sumire Japanese Keyboard (repository: kazumaproject/japanesekeyboard) demonstrates that powerful Japanese IME functionality does not require cloud connectivity. By architecting every component—from dictionary lookup to AI candidate generation—as an offline operation, Sumire ensures that keystrokes and personal vocabulary never leave the device. This article examines the specific implementation details that enforce this zero-network, privacy-first design.
The Foundation: Zero Network Permissions in AndroidManifest.xml
Sumire’s privacy guarantee begins at the system level. The AndroidManifest.xml deliberately omits the INTERNET permission, ensuring Android’s sandbox prevents any outbound network requests from the keyboard service.
<!-- app/src/main/AndroidManifest.xml -->
<manifest ...>
<!-- No <uses-permission android:name="android.permission.INTERNET"/> -->
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
...
</manifest>
By restricting permissions to only VIBRATE and RECORD_AUDIO (for voice input features), Sumire eliminates the possibility of accidental or malicious data exfiltration through standard Android APIs.
Offline-Only Conversion Engine
At the core of Sumire’s input method lies the mozc engine—the same open-source project that powers Google Japanese IME—modified to operate without network dependencies.
Bundled Dictionary Architecture
All dictionary data ships inside the APK as static resources. Sumire includes multiple UT (User-Tested) dictionaries covering person names, places, Wikipedia entries, Neologd, and web-crawled terms. These are defined in pref_dictionary.xml and stored in the assets directory.
<!-- app/src/main/res/xml/pref_dictionary.xml -->
<PreferenceScreen ...>
<CheckBoxPreference
android:key="MOZCUT_PERSON_NAME"
android:title="Person Names Dictionary"/>
<CheckBoxPreference
android:key="MOZCUT_PLACES"
android:title="Places Dictionary"/>
...
</PreferenceScreen>
Runtime Dictionary Loading
The IMEService.kt class dynamically loads only the dictionaries enabled by user preferences. This selective loading minimizes memory footprint while maintaining the zero-network constraint.
// app/src/main/java/com/kazumaproject/markdownhelperkeyboard/ime_service/IMEService.kt
private fun loadEnabledDictionaries() {
if (mozcUTPersonName == true) kanaKanjiEngine.loadPersonNamesDictionary()
if (mozcUTPlaces == true) kanaKanjiEngine.loadPlacesDictionary()
if (mozcUTWiki == true) kanaKanjiEngine.loadWikiDictionary()
if (mozcUTNeologd == true) kanaKanjiEngine.loadNeologdDictionary()
if (mozcUTWeb == true) kanaKanjiEngine.loadWebDictionary()
}
All dictionary files are read from local storage via kanaKanjiEngine.initModel() calls, ensuring that conversion candidates derive purely from on-device data.
Encrypted User Data With Android Keystore
Sumire protects personal learning data—such as frequently used words and custom entries—using AES-256-GCM encryption backed by the Android Keystore system.
The CryptoManager.kt class handles all cryptographic operations. When a user adds a custom word, the plaintext is encrypted before touching disk:
// core/src/main/java/com/kazumaproject/core/domain/cryptoManager/CryptoManager.kt
fun encrypt(plaintext: ByteArray): ByteArray {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, getOrCreateSecretKey())
val iv = cipher.iv
val ciphertext = cipher.doFinal(plaintext)
// Prepend IV to ciphertext for storage
return iv + ciphertext
}
The secret key is generated and stored within the Android Keystore (AndroidKeyStore provider), ensuring it never exists in application memory as plaintext and cannot be extracted from the device. This hardware-backed encryption guarantees that even if the device storage is compromised, the user's Japanese input history remains unreadable.
Local AI Processing With Zenz Engine
For advanced prediction and candidate ranking, Sumire optionally integrates the Zenz AI engine—a native library that performs neural network inference entirely on-device.
The ZenzEngine.kt wrapper exposes functions like generate() and candidateEvaluate() that process input sequences using bundled .gguf model files. Because the library is compiled into the APK and operates solely on local CPU/NPU resources, it requires no network permissions:
// zenz/src/main/java/com/kazumaproject/zenz/ZenzEngine.kt
class ZenzEngine {
external fun generate(input: String, maxLength: Int): String
external fun candidateEvaluate(candidate: String, context: String): Float
init {
System.loadLibrary("zenz")
}
}
The Zenz engine accesses only the bundled model assets, maintaining the zero-network architecture while providing modern AI-enhanced Japanese input capabilities.
Secure Lifecycle Management
Sumire ensures that sensitive data does not persist in memory longer than necessary. When the input method service is destroyed, IMEService.kt explicitly releases all loaded dictionary resources:
// app/src/main/java/com/kazumaproject/markdownhelperkeyboard/ime_service/IMEService.kt
override fun onDestroy() {
super.onDestroy()
// Release all UT dictionaries to prevent memory leaks
kanaKanjiEngine.releasePersonNamesDictionary()
kanaKanjiEngine.releasePlacesDictionary()
kanaKanjiEngine.releaseWikiDictionary()
kanaKanjiEngine.releaseNeologdDictionary()
kanaKanjiEngine.releaseWebDictionary()
}
This cleanup routine, located at lines 1546-1550 of IMEService.kt, ensures that dictionary data is purged from the heap when the keyboard closes, minimizing the window for memory-based attacks.
Summary
Sumire implements privacy-first Japanese input with zero network permissions through these architectural safeguards:
- Manifest-level blocking: The
AndroidManifest.xmlexplicitly excludesINTERNETpermission, preventing any network access at the OS level. - Offline mozc engine: All conversion dictionaries ship as local assets;
IMEService.ktloads selected dictionaries viakanaKanjiEnginemethods without external API calls. - Hardware-backed encryption:
CryptoManager.ktuses AES-256-GCM with Android Keystore to encrypt user dictionaries and learning data. - On-device AI: The Zenz engine runs locally via native libraries, processing neural models without network I/O.
- Secure lifecycle: Explicit resource cleanup in
IMEService.onDestroy()prevents residual data in memory.
Frequently Asked Questions
How does Sumire handle Japanese conversion without internet access?
Sumire embeds the open-source mozc conversion engine and all required dictionaries directly into the APK. When you type, the kanaKanjiEngine processes input using locally stored dictionary files (Person-Name, Places, Wiki, Neologd, Web) loaded by IMEService.kt. Because all linguistic data resides on the device, the keyboard generates conversion candidates instantly without network latency or connectivity requirements.
What encryption method protects my personal dictionary in Sumire?
Sumire uses AES-256-GCM encryption via the CryptoManager.kt class. When you add a custom word, the plaintext is encrypted using a secret key stored in the Android Keystore. This hardware-backed key never leaves the secure module, ensuring that even if someone gains access to your device storage, they cannot decrypt your personal Japanese vocabulary or input history without the hardware key.
Can the Zenz AI feature in Sumire leak data to cloud servers?
No. The Zenz AI engine operates entirely offline through a native library (libzenz.so) compiled into the APK. The ZenzEngine.kt wrapper calls native methods like generate() and candidateEvaluate() that process input using bundled .gguf model files stored in the app’s assets. Since the AndroidManifest.xml lacks INTERNET permission and the native code performs no socket operations, Zenz cannot transmit data externally.
Why does Sumire explicitly release dictionaries when the keyboard closes?
Sumire calls kanaKanjiEngine.release*Dictionary() methods inside IMEService.onDestroy() (lines 1546-1550) to prevent memory leaks and reduce attack surface. By explicitly unloading Person-Name, Places, Wiki, Neologd, and Web dictionaries from heap memory when the input method service stops, Sumire ensures that sensitive linguistic data does not persist in RAM longer than necessary, protecting against memory dumping attacks.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →