apphx 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +11 -9
  2. package/dist/cli.js +41 -11
  3. package/dist/cli.js.map +1 -1
  4. package/dist/config.js +4 -2
  5. package/dist/config.js.map +1 -1
  6. package/dist/integrations.js +177 -91
  7. package/dist/integrations.js.map +1 -1
  8. package/dist/notify.js +267 -0
  9. package/dist/notify.js.map +1 -0
  10. package/dist/policy.js +3 -16
  11. package/dist/policy.js.map +1 -1
  12. package/dist/runner.js +119 -20
  13. package/dist/runner.js.map +1 -1
  14. package/dist/schemas.js +57 -2
  15. package/dist/schemas.js.map +1 -1
  16. package/dist/vendor/obfuscator/dist/config.js +119 -6
  17. package/dist/vendor/obfuscator/dist/config.js.map +1 -1
  18. package/dist/vendor/obfuscator/dist/dfa.js +528 -0
  19. package/dist/vendor/obfuscator/dist/dfa.js.map +1 -0
  20. package/dist/vendor/obfuscator/dist/flutter.js +3266 -0
  21. package/dist/vendor/obfuscator/dist/flutter.js.map +1 -0
  22. package/dist/vendor/obfuscator/dist/image-utils.js +335 -0
  23. package/dist/vendor/obfuscator/dist/image-utils.js.map +1 -0
  24. package/dist/vendor/obfuscator/dist/junk.js +1501 -0
  25. package/dist/vendor/obfuscator/dist/junk.js.map +1 -0
  26. package/dist/vendor/obfuscator/dist/lexicon-data.js +972 -0
  27. package/dist/vendor/obfuscator/dist/lexicon-data.js.map +1 -0
  28. package/dist/vendor/obfuscator/dist/obfuscate.js +51 -2
  29. package/dist/vendor/obfuscator/dist/obfuscate.js.map +1 -1
  30. package/dist/vendor/obfuscator/dist/objc-symbols.js +1819 -0
  31. package/dist/vendor/obfuscator/dist/objc-symbols.js.map +1 -0
  32. package/dist/vendor/obfuscator/dist/project-identity.js +852 -0
  33. package/dist/vendor/obfuscator/dist/project-identity.js.map +1 -0
  34. package/dist/vendor/obfuscator/dist/string-encrypt.js +330 -0
  35. package/dist/vendor/obfuscator/dist/string-encrypt.js.map +1 -0
  36. package/examples/android-java.factory.json +40 -5
  37. package/examples/expo.factory.json +68 -5
  38. package/examples/flutter.factory.json +40 -4
  39. package/examples/luggage.json +1 -0
  40. package/examples/multi-role-agents.md +129 -0
  41. package/examples/mygo.factory.json +54 -4
  42. package/examples/queue-loop-demo.md +146 -0
  43. package/examples/react-native.factory.json +58 -5
  44. package/examples/roles.example.json +31 -0
  45. package/examples/swift.factory.json +60 -5
  46. package/examples/uni-app.factory.json +58 -5
  47. package/package.json +1 -1
  48. package/skills/apphx/SKILL.md +39 -4
  49. package/skills/apphx/resources/requirement.json +1 -0
  50. package/skills/apphx-obfuscator/LICENSE +21 -0
  51. package/skills/apphx-obfuscator/SKILL.md +72 -0
@@ -4,15 +4,68 @@
4
4
  "allowLocalExecution": false,
5
5
  "allowCloudBuild": false,
6
6
  "packageTarget": "aab",
7
- "executionEnv": ["ANDROID_HOME", "ANDROID_DEVICE_ID"],
7
+ "obfuscation": {
8
+ "options": {
9
+ "code": {
10
+ "removeComments": true
11
+ }
12
+ }
13
+ },
14
+ "executionEnv": [
15
+ "ANDROID_HOME",
16
+ "ANDROID_DEVICE_ID"
17
+ ],
8
18
  "verification": {
9
- "prerequisites": [["node", "--version"], ["java", "-version"], ["adb", "version"]],
10
- "checks": [["npm", "run", "lint"], ["npm", "test", "--", "--runInBand"]],
11
- "runtime": [["npm", "run", "test:e2e", "--", "--platform", "android", "--device", "${ANDROID_DEVICE_ID}"]]
19
+ "prerequisites": [
20
+ [
21
+ "node",
22
+ "--version"
23
+ ],
24
+ [
25
+ "java",
26
+ "-version"
27
+ ],
28
+ [
29
+ "adb",
30
+ "version"
31
+ ]
32
+ ],
33
+ "checks": [
34
+ [
35
+ "npm",
36
+ "run",
37
+ "lint"
38
+ ],
39
+ [
40
+ "npm",
41
+ "test",
42
+ "--",
43
+ "--runInBand"
44
+ ]
45
+ ],
46
+ "runtime": [
47
+ [
48
+ "npm",
49
+ "run",
50
+ "test:e2e",
51
+ "--",
52
+ "--platform",
53
+ "android",
54
+ "--device",
55
+ "${ANDROID_DEVICE_ID}"
56
+ ]
57
+ ]
12
58
  },
13
59
  "packaging": {
14
60
  "provider": "local",
15
- "commands": [["./native/android/gradlew", "-p", "native/android", ":app:bundleRelease"]],
61
+ "commands": [
62
+ [
63
+ "./native/android/gradlew",
64
+ "-p",
65
+ "native/android",
66
+ ":app:bundleRelease"
67
+ ]
68
+ ],
16
69
  "artifactPath": "native/android/app/build/outputs/bundle/release/app-release.aab"
17
70
  }
18
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apphx",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Host-native harness for verified Flutter, Java Android, Swift, React Native, Expo, uni-app and MyGo macOS delivery",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,7 +32,7 @@ Read the bundled [requirement example](resources/requirement.json), resolving `r
32
32
 
33
33
  If the user supplied an existing task id or asked to resume it, including a scheduled wake, **skip enqueue**. Read `status` and continue that exact id/config with `next`; never reconstruct its requirement or create a second task for the same work.
34
34
 
35
- The requirement must describe a real audience, purpose, differentiators, framework, explicit `platform` (`ios`, `android` or `macos`), neutral top-level `version`, `channel` (`local`, `app-store`, `google-play`) and concrete acceptance scenarios/constraints. `version` defaults to `1.0.0` and `channel` to `local`; neither belongs in `apple`. `screenshotsDir` is also top-level. Java Android is Android-only; Swift is iOS-only; MyGo is macOS/local-only; App Store requires iOS and Google Play requires Android/AAB. Preserve the user's product; do not substitute a simpler app. Require honest privacy behavior, no hidden features/review bypass and real screenshots. The optional top-level `apple` metadata block is owner documentation for a manual store submission, never a requirement or a claim of approval. Shared purpose/audience is not a duplicate-admission error; assess meaningful distinction in the selected channel's research. Do not invent ownership, account eligibility, signing credentials or legal attestations.
35
+ The requirement must describe a real audience, purpose, differentiators, framework, explicit `platform` (`ios`, `android` or `macos`), neutral top-level `version`, `channel` (`local`, `app-store`, `google-play`) and concrete acceptance scenarios/constraints. `version` defaults to `1.0.0`, `channel` to `local` and optional `priority` to `P1` (`P0` highest, `P2` lowest); priority only orders the `next --any` queue scan, it never overrides eligibility, budgets or acceptance gates. Neither belongs in `apple`. `screenshotsDir` is also top-level. Java Android is Android-only; Swift is iOS-only; MyGo is macOS/local-only; App Store requires iOS and Google Play requires Android/AAB. Preserve the user's product; do not substitute a simpler app. Require honest privacy behavior, no hidden features/review bypass and real screenshots. The optional top-level `apple` metadata block is owner documentation for a manual store submission, never a requirement or a claim of approval. Shared purpose/audience is not a duplicate-admission error; assess meaningful distinction in the selected channel's research. Do not invent ownership, account eligibility, signing credentials or legal attestations.
36
36
 
37
37
  ```sh
38
38
  apphx enqueue /absolute/control/requirement.json --config /absolute/control/factory.json --build-only
@@ -44,7 +44,7 @@ Keep the returned immutable job id and workspace. New projects have no `project`
44
44
 
45
45
  For **iOS App Store releases only**, omitting both `baseJobId` and `project.sourceDir` automatically pins the most recently enqueued finished same-product/platform/channel predecessor (`built`, `ready`, `approved` or `released`), recording `baseJobId`, `baseDigest` and `automaticBase: true`. Explicit source/base selection keeps its existing precedence and validation. The latest matching predecessor must have the same framework and valid unchanged source/dependency proof; a stale or incompatible latest version stops rather than falling back to an older version or starting fresh. Prior same-product history without a finished predecessor also stops; only a first-ever release without history may start new. Do not ask the owner to classify each release as new/update or manually copy code. A previous release is a source reuse baseline when available, not a differentiation unit; prior versions are release context, not competing portfolio products requiring artificial differentiation. Preserve application identity and data compatibility. Reuse and minimal changes are preferences, not a ban on theme, display-name, content, function or architecture changes needed for the product and its actual quality gaps.
46
46
 
47
- For a batch, enqueue the entire requested set before starting research so pending peers are visible to every review. With a configured inbox, submit its individual requirements and run `apphx scan --config /absolute/control/factory.json --build-only`; otherwise enqueue each requirement once and retain its returned job id. Drive each id through `next` and its returned action. A repair verdict is a continuation of that same job, not a request for manual classification, owner-written code or a replacement task. Continue eligible jobs while another is blocked or deferred, respect each due time and existing host scheduling/attempt limits, and never reuse one package's review for the batch.
47
+ For a batch, enqueue the entire requested set before starting research so pending peers are visible to every review. With a configured inbox, submit its individual requirements and run `apphx scan --config /absolute/control/factory.json --build-only`; otherwise enqueue each requirement once and retain its returned job id. Drive each id through `next` and its returned action, or drive the whole queue uniformly with `apphx next --any --config /absolute/control/factory.json --build-only`, which returns the earliest-due actionable task; a single job is just a queue of one. A repair verdict is a continuation of that same job, not a request for manual classification, owner-written code or a replacement task. Continue eligible jobs while another is blocked or deferred, respect each due time and existing host scheduling/attempt limits, and never reuse one package's review for the batch.
48
48
 
49
49
  Research and review compare the same-channel portfolio including pending batch peers, using their supplied versions and available read-only source. If `next` or `complete` rejects changed peer context, abandon that exact stale token and request a fresh turn within existing budgets; do not reuse its verdict or create a replacement task. Packaging rechecks the accepted comparison before and after commands. A peer-only change returns unchanged own source to review rather than discarding its runtime proof. Scheduling notes and unrelated-channel/same-product history are not peer-comparison changes.
50
50
 
@@ -57,6 +57,41 @@ Read the JSON from `next`; do not guess stages from memory. Use `--build-only` t
57
57
  - **`action: "wait"`**: inspect the job's status, reason, error and due time. Report the exact blocker or scheduled time; do not busy-loop, automatically grant permission, or retry a failed command until it happens to pass. Resume with `next` after the prerequisite/due time is satisfied and the user intends to continue.
58
58
  - **`action: "done"`**: inspect the persisted outcome. `built` plus a recorded local artifact is successful local packaging. Blocked/failed/rejected outcomes are not success merely because no action remains. Report the artifact kind/path/digest and evidence, or the exact unmet requirement.
59
59
 
60
+ ### Queue loop (single jobs and batches)
61
+
62
+ For unattended, batch or multi-requirement operation, loop the whole queue instead of enumerating ids yourself. The scan is ordered by priority lane (`P0` first, then `P1`, `P2`) and then due time; a deferred higher-priority job is skipped and the loop continues to the next safe action.
63
+
64
+ ```sh
65
+ apphx next --any --config /absolute/control/factory.json --build-only
66
+ ```
67
+
68
+ - Returns `{action:"host", job, turn}` for the earliest-due host turn: perform that one phase and finish with `complete`, exactly as for `next <id>`.
69
+ - Returns `{action:"step", job}` for the earliest-due deterministic stage: run `apphx step JOB_ID --config /absolute/control/factory.json --build-only` once, then iterate.
70
+ - Returns `{action:"idle", nextAt, pendingErrors, summary}` when nothing is due: read `summary` first for the terminal audit — the queue is truly finished only when every job is `built` or `failed` and no new requirements exist; a queue with `blocked`/`waiting`/`queued`/`running` jobs is merely stopped, not done. Inspect and report `pendingErrors` (stale or changed turn context that blocked a claim), then end this wake. `nextAt` is the earliest future due time; let the host scheduler resume then — never busy-loop, run `step` repeatedly on your own, or extend a loop past the owner-requested bound.
71
+
72
+ Repeat until `idle` or the requested bound. Research, build and review turns all flow through the same loop, so the whole pipeline advances queue-wide. Every iteration re-reads the persisted packet from `next --any`; never plan the next action from memory of the previous one. Blocked jobs never stall the queue: the scan continues to the next safe action. The queue worker lock and per-job turn tokens already serialize deterministic stages; schedule one queue-owning session at a time. An active turn found in the queue resumes under the same rules as `next <id>`; if the prior session's completion is unconfirmed, follow the scheduled-wake rule below.
73
+
74
+ - After each `step` or `complete` that returns a `built` job, or right before ending an `idle` wake, email authors with `apphx notify --any --config /absolute/control/factory.json --build-only`. It sends only to gate-passed (`built`) tasks that were not yet notified, is idempotent (receipt persisted in task state and events), and records failures as `notifyError` for retry. Configure `notify.email` (`from`, `to`, `smtpHost`, `smtpPort`, optional `secure`, `user`, `passwordEnv`, `subject`, `body`) in `factory.json`; a requirement's optional `authorEmail` overrides `to`. The SMTP password is read from the named environment variable at send time and never stored or printed. Notification is a local courtesy, never a store submission.
75
+
76
+ Keep a stable re-entry instruction for scheduled wakes: it only names the control project and `factory.json` and says to run `apphx status`/`apphx next --any` and follow this loop; never bake job ids, prompts, verdicts or task state into it — the current packet is always re-read from the CLI at wake time. A complete three-requirement walkthrough from enqueue to built lives in the shipped package example `examples/queue-loop-demo.md` (package root, next to this skill's package).
77
+
78
+ ### Multi-role team (roles on every host turn)
79
+
80
+ Every host turn payload carries `role` — the ordered list of agents to adopt for that stage (defaults: research `["product-analyst"]`; build `["developer"]`; review `["architect-review", "qa-engineer", "acceptance-reviewer"]`). Override or add roles per factory with `roles: { "research": [...], "build": [...], "review": [...] }` — role names are free-form, so missing agents (e.g. `security-review`, `performance-analyst`) are added in the config, not in the CLI.
81
+
82
+ **Role adoption is host-capability-aware.** Run `apphx hosts` and read the `delegation` note for your host:
83
+
84
+ - Hosts with native subagents (Codex, Claude Code, Hermes; others per their note): adopt all turn roles inside one session — parallel where roles only need fresh contexts, serial where one role consumes another's output. Still disclose which roles shared a context.
85
+ - Hosts without subagents (stock Pi, Gemini; Cursor/OpenCode per version): run each role in its **own fresh session**, one after another, reading the same job id/config/turn. Exactly one session completes the token.
86
+ - Independence is never downgraded silently: if a review role cannot run in a fresh non-implementer context, pause and ask the owner for that session (stock Pi rule above), or have another host run it. Do not fabricate independence.
87
+
88
+ **Review verdict aggregation**: the completing session merges the typed verdicts of all review roles — union of `findings`, and `acceptanceEvidence` must still cover every acceptance requirement with attributable concrete evidence (record which role observed what). Approve only when no role has blocking findings; a role with zero attributable evidence is a missing role, not a pass.
89
+
90
+ - **product-analyst (research)**: requirement grooming, feasibility, honest differentiation, privacy and plan; output is the typed research verdict.
91
+ - **developer (build)**: first produce the architecture design — run 2-3 independent design contexts (different models/providers when the host supports it), record each proposal plus a critique and the synthesized decision in `docs/architecture.md` inside the workspace, then implement the request in parallel module slices under the decided design, recording which slice follows which decision.
92
+ - **architect-review / qa-engineer / acceptance-reviewer (review)**: three fresh independent contexts that did not implement the change. The architect checks implementation against `docs/architecture.md` and module boundaries; the QA engineer checks empty/error states, accessibility, edge cases and quality; the acceptance reviewer covers every acceptance requirement with concrete observed evidence and issues the typed verdict. Exactly one session completes the token.
93
+ - **PM (no host turn)**: project management is the deterministic control plane — `next --any` ordering (priority lane, due time, insertion), budgets, retries, repair loops, `notify --any` and the terminal audit. Work assignment is computed by the queue, never delegated to a model, so it stays auditable and replayable. Which model each role runs on is a host decision (see the shipped example `examples/multi-role-agents.md`); apphx only names roles.
94
+
60
95
  For a scheduled wake, the host scheduler owns timing and non-overlap; do not create an apphx daemon or a second schedule. Check `status` first: if it is `running` and the prior execution's completion is unconfirmed, stop and ask the owner rather than taking over its token. Respect the requested bound (for example one persisted action), then report state and exit. A `wait` or `done` result ends this wake; it does not authorize changing permissions, increasing budgets or polling indefinitely. A fresh scheduled session may review only if it did not implement the change and genuinely inspects the saved source/evidence. Missing independent review or human authorization pauses the work; never manufacture approval to keep a schedule moving.
61
96
 
62
97
  To stop an active host phase without completing it:
@@ -134,9 +169,9 @@ The deterministic `step` executes configured prerequisite/static/runtime checks
134
169
 
135
170
  ### Optional deterministic obfuscation (runs between build and verify)
136
171
 
137
- When the factory config sets `obfuscation`, completing the build turn advances to an `obfuscate` step before verification: the runner executes `obfuscation.command` (default `obfuscator`) with `--project <workspace project> --config <obfuscation.configPath> --framework <stack>` and the optional `--seed`, so the verified and packaged code is the obfuscated code. `configPath` is resolved against the factory config directory and must exist as a regular file. The obfuscated source is exactly what verify checks, review approves and package ships; obfuscation never runs after verification.
172
+ When the factory config sets `obfuscation`, completing the build turn automatically advances to an `obfuscate` step before verification (no per-requirement work): the runner executes `obfuscation.command` (default `obfuscator`) with `--project <workspace project> --config <rules file> --framework <stack>` and the optional `--seed`, so the verified and packaged code is the obfuscated code. Rules come from exactly one of `obfuscation.configPath` (resolved against the factory config directory; must exist as a regular file) or `obfuscation.options` (inline rules of the same shape; the harness materializes a temporary snapshot that is removed after the stage, and the bundled obfuscator validates the rules at run time). `apphx init` writes a conservative default block (`options: { code: { removeComments: true } }`) into new factory files; delete it or strengthen it per project. The obfuscated source is exactly what verify checks, review approves and package ships; obfuscation never runs after verification.
138
173
 
139
- The bundled `obfuscator` package is deterministic: identical project + config + seed produce byte-identical output, in place, never writing outside the walked owned project. Installing `apphx` also installs the `obfuscator` executable (the package ships the private obfuscator), and `apphx obfuscator <project> --config <file> [--framework <stack>] [--seed <text>]` runs it directly; the harness default `obfuscation.command` resolves to that bundled executable. Point `obfuscation.command` at a different binary only to override it. Its JSON config supports:
174
+ The bundled `obfuscator` package is deterministic: identical project + config + seed produce byte-identical output, in place, never writing outside the walked owned project. Installing `apphx` also installs the `obfuscator` executable (the package ships the private obfuscator), and `apphx obfuscator <project> --config <file> [--framework <stack>] [--seed <text>]` runs it directly; the harness default `obfuscation.command` resolves to that bundled executable. Point `obfuscation.command` at a different binary only to override it. To run the same obfuscator directly outside a job, trigger the companion apphx-obfuscator skill (installed together with apphx). Its JSON config supports:
140
175
  - `project`: `name: { from, to }` (whole-word display-name scrub in framework config files), `directories: [...]` (seeded directory renames with reference rewrites), `uuids: true` (Xcode-UUID remapping that preserves the internal reference graph)
141
176
  - `resources`: `rename: { mappings, generated }` (asset stem renames with reference rewrites; `generated` deterministically renames that many discovered stems to seed-derived tokens), `hashPerturb: { bytes }` (decode-safe hash perturbation for PNG/JPEG/GIF), `images: { recolor, recolorAll }` (PNG color remap / hue-shift / invert / channel order)
142
177
  - `content`: `sensitiveWords`, `controlColors`, `fontColors` (whole-word scrub and hex-color remap across text and code, never binary payloads)
@@ -5,6 +5,7 @@
5
5
  "platform": "ios",
6
6
  "channel": "local",
7
7
  "version": "1.0.0",
8
+ "priority": "P1",
8
9
  "screenshotsDir": "build/screenshots",
9
10
  "purpose": "离线规划多个行李包的重量,完成出发打包与返程核对,减少遗漏和超重",
10
11
  "audience": "需要管理随身包与托运行李的个人旅客",
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Apple App Factory contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: apphx-obfuscator
3
+ description: Run the deterministic apphx obfuscator directly on an owned project copy from a JSON rules file. Supports ObjC/Swift symbol renaming, XOR string encryption, Flutter symbol/resource obfuscation, DFA sensitive-word filtering, junk code/files, resource cleaning, project identity renames and image transforms. Use for review-hardening a local app before verification or packaging; never use it to evade lawful review of code the owner cannot lawfully ship.
4
+ ---
5
+
6
+ # apphx-obfuscator
7
+
8
+ Run the bundled deterministic obfuscator on a project you own. Same CLI, config and determinism contract as the `obfuscate` stage of the apphx pipeline, but triggered directly, without enqueuing a job.
9
+
10
+ ## Prerequisites and command form
11
+
12
+ The `apphx` CLI owns the bundled obfuscator. Use the project's package manager form when `apphx` is a local dependency: `pnpm exec apphx`, `yarn exec apphx` or `npm exec --offline --package=apphx -- apphx`. Confirm with `apphx --version`, then run:
13
+
14
+ ```text
15
+ apphx obfuscator <project> --config <rules.json> [--framework <stack>] [--seed <text>]
16
+ ```
17
+
18
+ - `<project>`: absolute or relative path to the project copy to transform in place.
19
+ - `--config`: obfuscator rules as JSON (required; see below). Relative paths resolve against the current directory.
20
+ - `--framework`: `flutter|android-java|swift|react-native|expo|uni-app|mygo` (required unless set in config).
21
+ - `--seed`: determinism seed. Defaults to `config.seed`, then a hash of the rules file.
22
+
23
+ ## Rules file
24
+
25
+ JSON matching the obfuscator schema. At least one section must be present; every subsection is optional and empty subsections are no-ops.
26
+
27
+ ```json
28
+ {
29
+ "framework": "swift",
30
+ "seed": "my-seed",
31
+ "code": {
32
+ "removeComments": true,
33
+ "encryptStrings": { "excludes": [] },
34
+ "renameIdentifiers": { "mappings": {}, "patterns": [], "generated": 32 },
35
+ "junkCode": { "lines": 2 },
36
+ "keywords": {}
37
+ },
38
+ "resources": {
39
+ "rename": { "mappings": {}, "generated": 64 },
40
+ "hashPerturb": { "bytes": 16 },
41
+ "images": { "recolor": [{ "from": "#ff0000", "to": "#00ff00", "tolerance": 20 }], "scale": { "width": 512 }, "flatten": { "bgColor": "#ffffff" } }
42
+ },
43
+ "project": { "name": { "from": "Demo", "to": "Acme" }, "directories": [], "uuids": true },
44
+ "content": { "sensitiveWords": {}, "dfa": { "words": [], "replace": "*", "keywordMix": { "prefix": "zm" } } },
45
+ "objc": {
46
+ "symbols": { "algorithmType": 0, "methodPrefix": "zk", "propertyPrefix": "zk", "classPrefix": "ZF", "replaceClassPrefix": { "from": "Demo", "to": "ZF" }, "magicColor": {} },
47
+ "stringEncryption": { "encryptionNum": 77, "encryptedName": "NNC_NSSTRING" },
48
+ "junkCode": { "amount": 2, "rubPrefix": "OBFObj" },
49
+ "junkFiles": { "count": 2, "amount": 1, "language": "OC" },
50
+ "cleanResources": { "ignore": [] },
51
+ "projectIdentity": { "projectName": "Acme", "folderPrefix": "zk", "imagePrefix": "img", "imageAlgorithm": 0 }
52
+ },
53
+ "flutter": {
54
+ "prefixWord": "zk",
55
+ "missAlgorithmType": 0,
56
+ "classWhiteList": ["MyApp"],
57
+ "methodWhiteList": [],
58
+ "variableWhiteList": [],
59
+ "features": { "variable": true, "className": true, "method": true, "resourceName": true, "resourceHash": true, "deleteNotes": true, "uuid": true }
60
+ }
61
+ }
62
+ ```
63
+
64
+ `objc` covers iOS/ObjC/Swift symbol renaming, XOR string encryption with `.pch`/Swift decryptor injection, junk code/files, unused-resource cleaning and project identity renames (project name, folders, xcassets images, xib/storyboard references). `flutter` covers Dart class/method/variable renaming, resource names and hash perturbation, UUID remapping and comment removal. `content.dfa` is a DFA sensitive-word filter over a built-in lexicon plus your extra words.
65
+
66
+ ## Determinism and safety
67
+
68
+ - Identical project + rules + seed produce byte-identical output. No wall-clock, locale or `Math.random` state is consulted.
69
+ - The obfuscator only writes inside the walked project and skips generated/vendor directories. Run it on a **copy** of the project, never on the only checkout, unless the user explicitly says otherwise.
70
+ - Obfuscation changes the code that verification, review and packaging see. In the apphx pipeline, changing the obfuscation config invalidates prior verification proof and requires renewed verification and review.
71
+ - Keep the app buildable: after obfuscating, run the normal build/verify steps; any breakage is a rules-file problem to fix and retry.
72
+ - Do not weaken acceptance, verification or signing to accommodate obfuscation output.