# heyaibi/s4 architecture

> Architecture diagram of the GitHub repository heyaibi/s4, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-13). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/heyaibi/s4
- Repository: https://github.com/heyaibi/s4

## Overview

#### Purpose and Runtime

S4 is a fully offline Android backup application for converting BIP-39 wallet entropy into threshold SLIP-39 paper mnemonic shares and restoring it later. It targets Android API 26–37 and uses Kotlin, Jetpack Compose, Material 3, Navigation Compose, NDK/CMake, and JNI. No backend or network service is part of the architecture.

Entry point: `app/src/main/java/com/s4/MainActivity.kt`. Android packaging and security constraints, including no network permission and backup restrictions, are in `app/src/main/AndroidManifest.xml`.

#### Application Navigation and Screen Boundary

`MainActivity` owns the Compose navigation graph and app-level secure-screen behavior. Routes separate the principal workflows: Split, Restore, results, and Recovery Guide.

- `app/src/main/java/com/s4/navigation/Routes.kt`
- `app/src/main/java/com/s4/ui/SplitScreen.kt`
- `app/src/main/java/com/s4/ui/RestoreScreen.kt`

UI state is memory-only: secret-bearing form values are not intended for persisted saved-state or disk storage. Result screens retain an in-memory session until completion, then discard it.

#### Split Workflow

The Split screen collects either BIP-39 mnemonic words or raw entropy, an optional BIP-39 passphrase, and threshold/count parameters. `SplitViewModel` validates and orchestrates the conversion pipeline:

1. parse or generate entropy;
2. derive/display a fingerprint;
3. call SLIP-39 native generation;
4. expose stable mnemonic shares and an optional recovery guide.

- `app/src/main/java/com/s4/ui/SplitViewModel.kt`
- `app/src/main/java/com/s4/model/SplitParams.kt`
- `app/src/main/java/com/s4/ui/SplitResultScreen.kt`

The app shards entropy, not the PBKDF2 wallet seed. The BIP-39 passphrase remains separate from shares by design.

#### Restore Workflow

Restore accepts one share per line, supports SLIP-39 word completion, parses and validates shares, then combines a qualifying threshold into entropy. That entropy is converted back to BIP-39 words and fingerprinted; the entered passphrase can be verified against the expected fingerprint.

- `app/src/main/java/com/s4/ui/RestoreScreen.kt`
- `app/src/main/java/com/s4/crypto/ShareCodec.kt`
- `app/src/main/java/com/s4/ui/WordCompletion.kt`

Errors at this boundary include malformed/checksum-invalid shares, insufficient shares, and incompatible share sets; these are surfaced through Kotlin exception types rather than leaking native details.

#### Kotlin Crypto Domain Layer

Kotlin provides the application-facing crypto API, input/result shaping, exception handling, and wordlist access. BIP-39 handles entropy-to-mnemonic conversion, seed derivation, and SHA-256 fingerprinting. SLIP-39 and Shamir wrappers bridge to the native implementation.

- `app/src/main/java/com/s4/bip39/Bip39.kt`
- `app/src/main/java/com/s4/crypto/Slip39.kt`
- `app/src/main/java/com/s4/crypto/Shamir.kt`

Static BIP-39 and SLIP-39 vocabularies are bundled locally in `app/src/main/resources/wordlist.txt` and `app/src/main/resources/slip39-wordlist.txt`.

#### Native Cryptography Boundary

Cryptographic splitting and combination execute in vendored C libraries compiled through CMake/Android NDK. Thin JNI facades define the Kotlin/native boundary. SLIP-39 uses a single configured group and native randomness is supplied from Android `SecureRandom` through callbacks.

- `app/src/main/cpp/CMakeLists.txt`
- `app/src/main/cpp/slip39_jni.cpp`
- `app/src/main/cpp/shamir_jni.cpp`

Vendored primitives and algorithms reside under `app/src/main/cpp/bc-slip39`, `app/src/main/cpp/bc-shamir`, and `app/src/main/cpp/bc-crypto-base`; this avoids OpenSSL and external crypto services.

#### Recovery and Device Provisioning

The Recovery Guide produces durable, plain-language recovery instructions based on the active split session, including the requirement to preserve the BIP-39 passphrase separately.

- `app/src/main/java/com/s4/guide/RecoveryGuide.kt`
- `app/src/main/java/com/s4/ui/RecoveryGuideScreen.kt`

Outside the app, one-shot ADB provisioning hardens the dedicated phone, installs specified APKs with checksum verification, disables radios/bloatware, and configures prerequisites for the anti-forensics deployment model.

- `airgap/airgap.sh`
- `airgap/apps.json`
- `airgap/packages.txt`

Main entry points: [`app/src/main/java/com/s4/navigation/Routes.kt`](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/navigation/Routes.kt) (Routes), [`app/src/main/java/com/s4/ui/SplitViewModel.kt`](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitViewModel.kt) (Split view model), [`app/src/main/java/com/s4/crypto/ShareCodec.kt`](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/ShareCodec.kt) (Share codec), [`app/src/main/cpp/slip39_jni.cpp`](https://github.com/heyaibi/s4/blob/master/app/src/main/cpp/slip39_jni.cpp) (JNI facades)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_android["Android app"]
  node_manifest["Packaging &amp; security<br/>Android manifest"]
  node_activity["Main activity<br/>Compose entry point<br/>[MainActivity.kt]"]
  node_routes["Routes<br/>navigation<br/>[Routes.kt]"]
  node_split_ui["Split workflow<br/>Compose screen<br/>[SplitScreen.kt]"]
  node_split_vm["Split view model<br/>workflow orchestrator<br/>[SplitViewModel.kt]"]
  node_split_params["Split parameters<br/>domain model<br/>[SplitParams.kt]"]
  node_split_result["Split results<br/>Compose screen"]
  node_restore_ui["Restore workflow<br/>Compose screen<br/>[RestoreScreen.kt]"]
  node_completion["Share word completion<br/>UI support<br/>[WordCompletion.kt]"]
  node_guide["Recovery guide<br/>recovery instructions<br/>[RecoveryGuide.kt]"]
  node_guide_ui["Recovery guide screen<br/>Compose screen"]
end

subgraph group_domain["Crypto domain"]
  node_share_codec["Share codec<br/>restore boundary<br/>[ShareCodec.kt]"]
  node_bip39["BIP-39<br/>Kotlin crypto API<br/>[Bip39.kt]"]
  node_slip39["SLIP-39 wrapper<br/>Kotlin JNI API<br/>[Slip39.kt]"]
  node_shamir["Shamir wrapper<br/>Kotlin JNI API<br/>[Shamir.kt]"]
  node_wordlists["Bundled wordlists<br/>local vocabulary<br/>[wordlist.txt]"]
end

subgraph group_native["Native crypto"]
  node_jni["JNI facades<br/>native boundary<br/>[slip39_jni.cpp]"]
  node_native_libs["Vendored crypto libraries<br/>C algorithms"]
  node_cmake["NDK build<br/>CMake build<br/>[CMakeLists.txt]"]
end

subgraph group_provisioning["Device provisioning"]
  node_airgap["Air-gap provisioning<br/>ADB hardening script<br/>[airgap.sh]"]
  node_apps["APK manifest<br/>provisioning input<br/>[apps.json]"]
  node_packages["Package policy<br/>provisioning input<br/>[packages.txt]"]
end

node_manifest -->|"packages and constrains"| node_activity
node_activity -->|"hosts"| node_routes
node_routes -->|"Split route"| node_split_ui
node_routes -->|"Restore route"| node_restore_ui
node_routes -->|"results route"| node_split_result
node_routes -->|"guide route"| node_guide_ui
node_split_ui -->|"submits inputs"| node_split_vm
node_split_vm -->|"uses"| node_split_params
node_split_vm -->|"parses entropy and fingerprints"| node_bip39
node_split_vm -->|"generates shares"| node_slip39
node_split_vm -->|"publishes session results"| node_split_result
node_split_result -->|"active split session"| node_guide
node_guide -->|"renders"| node_guide_ui
node_restore_ui -->|"assists entry"| node_completion
node_restore_ui -->|"validates and combines"| node_share_codec
node_share_codec -->|"parses SLIP-39"| node_slip39
node_share_codec -->|"combines threshold shares"| node_shamir
node_share_codec -->|"restores mnemonic and fingerprint"| node_bip39
node_bip39 -->|"loads BIP-39 words"| node_wordlists
node_slip39 -->|"calls native generation"| node_jni
node_shamir -->|"calls native combination"| node_jni
node_jni -->|"executes"| node_native_libs
node_cmake -->|"builds"| node_jni
node_cmake -->|"compiles"| node_native_libs
node_airgap -->|"verifies and installs"| node_apps
node_airgap -->|"disables/configures"| node_packages

click node_manifest "https://github.com/heyaibi/s4/blob/master/app/src/main/AndroidManifest.xml"
click node_activity "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/MainActivity.kt"
click node_routes "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/navigation/Routes.kt"
click node_split_ui "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitScreen.kt"
click node_split_vm "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitViewModel.kt"
click node_split_params "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/model/SplitParams.kt"
click node_split_result "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitResultScreen.kt"
click node_restore_ui "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/RestoreScreen.kt"
click node_share_codec "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/ShareCodec.kt"
click node_completion "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/WordCompletion.kt"
click node_bip39 "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/bip39/Bip39.kt"
click node_slip39 "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/Slip39.kt"
click node_shamir "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/Shamir.kt"
click node_wordlists "https://github.com/heyaibi/s4/blob/master/app/src/main/resources/wordlist.txt"
click node_jni "https://github.com/heyaibi/s4/blob/master/app/src/main/cpp/slip39_jni.cpp"
click node_native_libs "https://github.com/heyaibi/s4/tree/master/app/src/main/cpp/bc-slip39"
click node_cmake "https://github.com/heyaibi/s4/blob/master/app/src/main/cpp/CMakeLists.txt"
click node_guide "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/guide/RecoveryGuide.kt"
click node_guide_ui "https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/RecoveryGuideScreen.kt"
click node_airgap "https://github.com/heyaibi/s4/blob/master/airgap/airgap.sh"
click node_apps "https://github.com/heyaibi/s4/blob/master/airgap/apps.json"
click node_packages "https://github.com/heyaibi/s4/blob/master/airgap/packages.txt"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_manifest,node_activity,node_routes,node_split_ui,node_split_vm,node_split_params,node_split_result,node_restore_ui,node_completion,node_guide,node_guide_ui toneBlue
class node_share_codec,node_bip39,node_slip39,node_shamir,node_wordlists toneAmber
class node_jni,node_native_libs,node_cmake toneMint
class node_airgap,node_apps,node_packages toneRose
```

## Components

### Android app

- [Packaging & security](https://github.com/heyaibi/s4/blob/master/app/src/main/AndroidManifest.xml): Android manifest · Blocks network access and restricts backups. · `app/src/main/AndroidManifest.xml`
- [Main activity](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/MainActivity.kt): Compose entry point · Owns navigation and secure-screen behavior. · `app/src/main/java/com/s4/MainActivity.kt`
- [Routes](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/navigation/Routes.kt): navigation · `app/src/main/java/com/s4/navigation/Routes.kt`
- [Split workflow](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitScreen.kt): Compose screen · Collects entropy or mnemonic, passphrase, and share parameters. · `app/src/main/java/com/s4/ui/SplitScreen.kt`
- [Split view model](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitViewModel.kt): workflow orchestrator · Keeps secret-bearing state in memory only. · `app/src/main/java/com/s4/ui/SplitViewModel.kt`
- [Split parameters](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/model/SplitParams.kt): domain model · `app/src/main/java/com/s4/model/SplitParams.kt`
- [Split results](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/SplitResultScreen.kt): Compose screen · Displays stable shares for the active session. · `app/src/main/java/com/s4/ui/SplitResultScreen.kt`
- [Restore workflow](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/RestoreScreen.kt): Compose screen · Accepts one SLIP-39 share per line. · `app/src/main/java/com/s4/ui/RestoreScreen.kt`
- [Share word completion](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/WordCompletion.kt): UI support · `app/src/main/java/com/s4/ui/WordCompletion.kt`
- [Recovery guide](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/guide/RecoveryGuide.kt): recovery instructions · Preserves the requirement to store the BIP-39 passphrase separately. · `app/src/main/java/com/s4/guide/RecoveryGuide.kt`
- [Recovery guide screen](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/ui/RecoveryGuideScreen.kt): Compose screen · `app/src/main/java/com/s4/ui/RecoveryGuideScreen.kt`

### Crypto domain

- [Share codec](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/ShareCodec.kt): restore boundary · Validates, parses, and combines shares without exposing native errors. · `app/src/main/java/com/s4/crypto/ShareCodec.kt`
- [BIP-39](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/bip39/Bip39.kt): Kotlin crypto API · Converts entropy and mnemonics, derives seeds, and fingerprints entropy. · `app/src/main/java/com/s4/bip39/Bip39.kt`
- [SLIP-39 wrapper](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/Slip39.kt): Kotlin JNI API · `app/src/main/java/com/s4/crypto/Slip39.kt`
- [Shamir wrapper](https://github.com/heyaibi/s4/blob/master/app/src/main/java/com/s4/crypto/Shamir.kt): Kotlin JNI API · `app/src/main/java/com/s4/crypto/Shamir.kt`
- [Bundled wordlists](https://github.com/heyaibi/s4/blob/master/app/src/main/resources/wordlist.txt): local vocabulary · Static BIP-39 and SLIP-39 vocabularies. · `app/src/main/resources/wordlist.txt`

### Native crypto

- [JNI facades](https://github.com/heyaibi/s4/blob/master/app/src/main/cpp/slip39_jni.cpp): native boundary · Bridges Kotlin wrappers to native splitting and combination. · `app/src/main/cpp/slip39_jni.cpp`
- [Vendored crypto libraries](https://github.com/heyaibi/s4/tree/master/app/src/main/cpp/bc-slip39): C algorithms · SLIP-39, Shamir, and base primitives; no OpenSSL. · `app/src/main/cpp/bc-slip39`
- [NDK build](https://github.com/heyaibi/s4/blob/master/app/src/main/cpp/CMakeLists.txt): CMake build · `app/src/main/cpp/CMakeLists.txt`

### Device provisioning

- [Air-gap provisioning](https://github.com/heyaibi/s4/blob/master/airgap/airgap.sh): ADB hardening script · One-shot dedicated-device provisioning and APK verification. · `airgap/airgap.sh`
- [APK manifest](https://github.com/heyaibi/s4/blob/master/airgap/apps.json): provisioning input · Specified APKs and checksums. · `airgap/apps.json`
- [Package policy](https://github.com/heyaibi/s4/blob/master/airgap/packages.txt): provisioning input · Radios and bloatware targets. · `airgap/packages.txt`

## Connections

- Packaging & security → Main activity: packages and constrains
- Main activity → Routes: hosts
- Routes → Split workflow: Split route
- Routes → Restore workflow: Restore route
- Routes → Split results: results route
- Routes → Recovery guide screen: guide route
- Split workflow → Split view model: submits inputs
- Split view model → Split parameters: uses
- Split view model → BIP-39: parses entropy and fingerprints
- Split view model → SLIP-39 wrapper: generates shares
- Split view model → Split results: publishes session results
- Split results → Recovery guide: active split session
- Recovery guide → Recovery guide screen: renders
- Restore workflow → Share word completion: assists entry
- Restore workflow → Share codec: validates and combines
- Share codec → SLIP-39 wrapper: parses SLIP-39
- Share codec → Shamir wrapper: combines threshold shares
- Share codec → BIP-39: restores mnemonic and fingerprint
- BIP-39 → Bundled wordlists: loads BIP-39 words
- SLIP-39 wrapper → JNI facades: calls native generation
- Shamir wrapper → JNI facades: calls native combination
- JNI facades → Vendored crypto libraries: executes
- NDK build → JNI facades: builds
- NDK build → Vendored crypto libraries: compiles
- Air-gap provisioning → APK manifest: verifies and installs
- Air-gap provisioning → Package policy: disables/configures
