{"id":47706185,"url":"https://github.com/afonsomota/flutter_concrete","last_synced_at":"2026-04-02T17:58:53.330Z","repository":{"id":345155550,"uuid":"1183370403","full_name":"afonsomota/flutter_concrete","owner":"afonsomota","description":"A Flutter FFI plugin that brings Concrete ML FHE (Fully Homomorphic Encryption) to mobile apps. The native cryptographic operations — key generation, encryption, and decryption — run entirely on-device via TFHE-rs, with no server-side private key material.","archived":false,"fork":false,"pushed_at":"2026-03-29T10:59:57.000Z","size":240,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-29T13:52:35.192Z","etag":null,"topics":["fhe","flutter","private-ai"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/flutter_concrete","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/afonsomota.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-16T14:40:29.000Z","updated_at":"2026-03-26T15:17:55.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/afonsomota/flutter_concrete","commit_stats":null,"previous_names":["afonsomota/flutter_concrete"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/afonsomota/flutter_concrete","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsomota%2Fflutter_concrete","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsomota%2Fflutter_concrete/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsomota%2Fflutter_concrete/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsomota%2Fflutter_concrete/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/afonsomota","download_url":"https://codeload.github.com/afonsomota/flutter_concrete/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/afonsomota%2Fflutter_concrete/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31236914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-31T09:14:28.471Z","status":"ssl_error","status_checked_at":"2026-03-31T09:14:19.506Z","response_time":111,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fhe","flutter","private-ai"],"created_at":"2026-04-02T17:58:52.818Z","updated_at":"2026-04-02T17:58:53.318Z","avatar_url":"https://github.com/afonsomota.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flutter_concrete\n\nA Flutter FFI plugin that brings [Concrete ML](https://docs.zama.ai/concrete-ml) FHE (Fully Homomorphic Encryption) to mobile apps. The native cryptographic operations — key generation, encryption, and decryption — run entirely on-device via [TFHE-rs](https://github.com/zama-ai/tfhe-rs), with no server-side private key material.\n\nThe Rust library builds automatically during `flutter build` thanks to [Cargokit](https://github.com/irondash/cargokit) — no manual build scripts or precompiled binaries required.\n\n## How it works\n\n```\nYour App                              flutter_concrete\n───────                               ────────────────\n                                           │\nLoad client.zip from assets ──────►  setup(zipBytes, storage)\n                                       parse serialized_processing.json\n                                       restore or generate keys\n                                     ◄── isReady = true\n                                           │\nGet serverKey ◄────────────────────  serverKey / serverKeyBase64\nUpload to your server (your code)          │\n                                           │\nFloat32 features ──────────────────►  quantizeAndEncrypt()\nUint8List ciphertext ◄──────────────       │\nSend to server (your code)                 │\nReceive result (your code)                 │\nUint8List encrypted result ────────►  decryptAndDequantize()\nFloat64 class scores ◄──────────────       │\nInterpret scores (your code)\n```\n\nThe server performs ML inference on **encrypted** data — it never sees plaintext inputs or predictions.\n\n## Installation\n\nAdd to your `pubspec.yaml`:\n\n```yaml\ndependencies:\n  flutter_concrete: ^0.3.0\n```\n\n### Prerequisites\n\n- **Rust toolchain** — install via [rustup](https://rustup.rs/)\n- iOS targets: `rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios`\n- Android targets: `rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android`\n\n## Usage\n\n```dart\nimport 'package:flutter_concrete/flutter_concrete.dart';\n\n// 1. Implement KeyStorage (e.g. wrapping flutter_secure_storage)\nclass MyKeyStorage implements KeyStorage {\n  @override\n  Future\u003cUint8List?\u003e read(String key) async { /* ... */ }\n  @override\n  Future\u003cvoid\u003e write(String key, Uint8List value) async { /* ... */ }\n  @override\n  Future\u003cvoid\u003e delete(String key) async { /* ... */ }\n}\n\n// 2. Create client and set up from Concrete ML's client.zip\nfinal client = ConcreteClient();\nfinal zipBytes = await loadClientZipFromAssets(); // your asset loading\nawait client.setup(\n  clientZipBytes: zipBytes,\n  storage: MyKeyStorage(),\n);\n// First call generates keys (~10-60s on mobile), subsequent calls restore.\n\n// 3. Get server key to upload to your backend\nfinal serverKey = client.serverKey;       // Uint8List\nfinal serverKeyB64 = client.serverKeyBase64; // String (cached)\n// Upload to your server however you want\n\n// 4. Encrypt features\nfinal ciphertext = client.quantizeAndEncrypt(featureVector);\n// Send ciphertext to your server for FHE inference\n\n// 5. Decrypt server response\nfinal scores = client.decryptAndDequantize(encryptedResult);\n// scores is Float64List — post-processed based on model type\n// For classifiers: probabilities (apply argmax for predicted class)\n// For regressors: predicted values\n```\n\n## API\n\n### `ConcreteClient`\n\n| Method | Description |\n|--------|-------------|\n| `Future\u003cvoid\u003e setup({clientZipBytes, storage})` | Parse `client.zip`, generate/restore keys |\n| `void reset()` | Clear state so `setup()` can be called with a different model |\n| `bool get isReady` | True after `setup()` completes |\n| `Uint8List get serverKey` | Raw evaluation key bytes (throws before setup) |\n| `String get serverKeyBase64` | Base64-encoded server key (cached) |\n| `Uint8List quantizeAndEncrypt(Float32List)` | Quantize + FHE encrypt |\n| `Float64List decryptAndDequantize(Uint8List, {PostProcessing})` | FHE decrypt + dequantize + post-process |\n| `String? get modelClassName` | Model class from `client.zip` (e.g. `\"XGBClassifier\"`) |\n| `PostProcessing get detectedPostProcessing` | Auto-resolved post-processing variant |\n\n### `KeyStorage` (abstract — you implement this)\n\n| Method | Description |\n|--------|-------------|\n| `Future\u003cUint8List?\u003e read(String key)` | Read stored bytes, or null |\n| `Future\u003cvoid\u003e write(String key, Uint8List value)` | Persist bytes |\n| `Future\u003cvoid\u003e delete(String key)` | Delete entry |\n\n## Post-processing\n\nAfter decryption and dequantization, `decryptAndDequantize` applies model-specific post-processing to match Python's `FHEModelClient.deserialize_decrypt_dequantize`. The model class name is parsed from `client.zip` and mapped to the correct transform automatically.\n\n### Auto-detection (default)\n\n```dart\n// PostProcessing.auto() is the default — no code changes needed.\nfinal scores = client.decryptAndDequantize(encryptedResult);\n\n// Check what was detected:\nprint(client.modelClassName);          // e.g. \"XGBClassifier\"\nprint(client.detectedPostProcessing);  // e.g. EnsembleClassifierPostProcessing\n```\n\n### Supported models\n\n| Model class | Variant | Behavior |\n|---|---|---|\n| `XGBClassifier` | `ensembleClassifier` | Sum trees → sigmoid/softmax |\n| `RandomForestClassifier`, `DecisionTreeClassifier` | `ensembleProbabilistic` | Sum trees (outputs are already probabilities) |\n| `XGBRegressor` | `xgbRegressor` | Sum trees + 0.5 bias |\n| `RandomForestRegressor`, `DecisionTreeRegressor` | `ensembleRegressor` | Sum trees |\n| `LogisticRegression`, `LinearSVC`, `SGDClassifier`, `NeuralNetClassifier` | `classifier` | sigmoid (2 classes) / softmax (\u003e2) |\n| `LinearRegression`, `Ridge`, `Lasso`, `ElasticNet`, `SGDRegressor`, `LinearSVR`, `NeuralNetRegressor` | `regressor` | Identity |\n\nUnknown models fall back to `PostProcessing.none()` with a logged warning.\n\n### Explicit override\n\n```dart\n// Force a specific post-processing variant:\nfinal scores = client.decryptAndDequantize(\n  encryptedResult,\n  postProcessing: const PostProcessing.classifier(),\n);\n\n// Skip post-processing entirely:\nfinal raw = client.decryptAndDequantize(\n  encryptedResult,\n  postProcessing: const PostProcessing.none(),\n);\n```\n\n### Custom post-processing\n\n```dart\n// For models not in the lookup table (e.g. GLM regressors needing exp):\nimport 'dart:math';\nfinal scores = client.decryptAndDequantize(\n  encryptedResult,\n  postProcessing: PostProcessing.custom((values, shape) {\n    return Float64List.fromList(values.map((v) =\u003e exp(v)).toList());\n  }),\n);\n```\n\n### Models requiring custom post-processing\n\n- `KNeighborsClassifier` — majority vote paradigm, use `PostProcessing.none()` or `PostProcessing.custom()`\n- `PoissonRegressor`, `GammaRegressor`, `TweedieRegressor` — need `exp()` inverse link\n- `SGDClassifier` with `modified_huber` loss — non-standard activation\n\n## Ciphertext formats\n\nThe plugin automatically detects the ciphertext format from `client.specs.json` inside `client.zip`:\n\n| Format | `n_bits` | Circuit size | Notes |\n|--------|----------|--------------|-------|\n| **CONCRETE** (default) | 1–7 | Small | Seeded LWE encryption, Cap'n Proto Value serialization |\n| **TFHE-RS** | 8 | Large | Raw TFHE-rs `FheUint8`/`FheInt8`, bincode serialization |\n\nNo code changes needed when switching formats — `ConcreteClient` routes through the appropriate path based on what the model was compiled with.\n\n## Compatibility\n\n- **Concrete ML**: Accepts standard `client.zip` from `FHEModelDev.save()`\n- **TFHE-rs**: Git revision `1ec21a5` (matching `concrete-ml-extensions` 0.2.0)\n- **Keygen parameters**: Derived dynamically from circuit topology\n- **Serialization**: Cap'n Proto (evaluation keys and CONCRETE ciphertexts), bincode (TFHE-RS ciphertexts)\n- **Platforms**: iOS, Android\n- **Encoding widths**: FheUint8–FheUint64, FheInt8–FheInt64 (selected automatically from `client.specs.json`)\n\n## Known limitations\n\n1. **Single input/output tensor** — assumes one input and one output tensor per circuit.\n2. **Native encoding mode only** — chunked and CRT encoding modes are not supported (fail-fast with `UnsupportedError`).\n3. **Single output quantizer** — only the first output quantizer is used. Models with per-output quantization (some neural networks) are not yet supported.\n4. **QuantizedModule / custom torch models** — arbitrary circuit outputs require `PostProcessing.none()` or `PostProcessing.custom()`.\n\n## License\n\nBSD-3-Clause\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafonsomota%2Fflutter_concrete","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fafonsomota%2Fflutter_concrete","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fafonsomota%2Fflutter_concrete/lists"}