Repository navigation
A large release. The Python package is reorganised, the web service moves behind an extra, and several latent bugs are fixed. Read the migration notes before upgrading.
Docker: ghcr.io/ykus4/fuin:v2.0.0
Documentation: https://ykus4.github.io/fuin
⚠️ Breaking changes
1. pip install fuin no longer installs the server
The base install is the packer alone — cryptography and python-dotenv, down from 12 packages. To get fuin-server:
pip install 'fuin[server]'2. Module paths changed
The flat package is now grouped by concern.
| Before | After |
|---|---|
fuin.crypto |
fuin.encryption.aes |
fuin.string_encrypt |
fuin.encryption.dex_strings |
fuin.native_lib |
fuin.encryption.native_libs |
fuin.resource_encrypt |
fuin.encryption.resources |
fuin.signing, fuin.zipalign, fuin.keystore, fuin.stub_dex |
fuin.apk.* |
fuin.android_tools |
fuin.apk.tools |
fuin.manifest.patch_manifest |
fuin.apk.patch_manifest |
fuin.manifest (byte level) |
fuin.axml.patcher |
fuin.apk_info |
fuin.axml.info |
fuin.analyze, fuin.report |
fuin.reporting.* |
fuin._constants |
fuin.contract (+ fuin.axml.constants, fuin.apk.constants) |
fuin.integrity |
removed — use fuin.apk.extract_cert_fingerprint |
fuin.server.models |
fuin.server.schemas |
3. Gradle composite-build path moved
// before
includeBuild("path/to/fuin/gradle-plugin")
// after
includeBuild("path/to/fuin/jvm/gradle-plugin")4. API changes
run_pipelinereturns aPackedOutputinstead of a(path, sha256, report)tuple.PipelineOptionsis removed; usePackOptions.- The Pydantic
PackResultis renamedPackedApp, to stop colliding with the packer'sPackResultdataclass.RegisterAppResponseis removed (it had no references). - Protection options are now tri-state. Omitting
--root-detection,--emulator-detection,--encrypt-strings,--verify-signatureor--no-strict-manifest-patchdefers to the matchingFUIN_*variable; passing one always wins. Previously an explicitFalsecould be overridden by the environment.
🐛 Fixed
- The published wheel could not pack.
assets/stub.dexsat outside the package directory and was never included, sopip install fuinhad no stub to inject. - The Docker image did not start. The build never installed the project, so the
fuin-serverconsole script did not exist and the container exited immediately. - Settings were captured at import time, so
FUIN_*changes needed a process restart, and every pipeline test wrote packed APKs into the repository instead of a temp directory. package_namecould be read from the wrong resource ID, returning an unrelated string-pool entry on APKs carrying aversionCode.version_codewas alwaysNone— initialised and never assigned.- Concurrent pack jobs clobbered each other through a single shared
.pending.apkscratch path. pipeline.pyre-listed all 13PackOptionsfields to override one, silently dropping any field added later.- The in-memory job store grew without bound, retaining every finished job's full result for the process lifetime.
- Detached asyncio tasks were created without holding a reference and could be garbage-collected mid-flight.
- A DEX between 44 and 63 bytes raised
struct.errorinstead of being skipped (off-by-one in the header bounds check). reset_enginedropped the engine without disposing its connection pool.- Dead code removed from the AXML patcher, including an authoring note left in a shipped source file.
✨ Added
- Documentation site at https://ykus4.github.io/fuin — 14 pages covering installation, all five interfaces, configuration, the API, architecture, security and development.
FUIN_MAX_MAPPING_MBfor the ProGuard mapping upload limit, previously hardcoded at 50 MB.- Tests for
signingandstring_encrypt, neither of which had any coverage. - mypy, running clean over the package, in CI and pre-commit.
- CI now tests Python 3.12, 3.13 and 3.14 (the Docker image ships 3.14 and had never been tested), measures coverage, runs the Docker image rather than only building it, exercises Alembic upgrade/downgrade and checks the migrated schema against the models, and verifies a packer-only wheel install can pack an APK.
🔧 Changed
- Adopted a src layout;
stub/andgradle-plugin/are grouped underjvm/. - The two AXML parsers — one in the inspector, one in the patcher — are unified into
fuin.axml. PKCS12 handling, external-tool invocation and ZIP-copy loops are likewise deduplicated. - The server gains router, repository and settings layers;
main.pydrops from 286 lines to app assembly only. action.ymlno longer installs the dev dependency group, and no longer breaks workspace-relative input/output paths.- README trimmed from 446 lines to an entry point, with the content moved to the documentation site.
Full changes: CHANGELOG · v1.1.2...v2.0.0