App Store Connect rejected the iOS upload with 'Invalid Export Compliance Code' because ITSAppUsesNonExemptEncryption was <true/> without an accompanying ITSEncryptionExportComplianceCode. Apply a TestFlight-only workaround now and document the full production path that needs to land before any public App Store / Mac App Store release. Info.plist (ios + macos) - ITSAppUsesNonExemptEncryption set to <false/> on both platforms to unblock TestFlight internal-test uploads. This is NOT correct for public release: Chanora ships non-system crypto (chacha20poly1305 in chanora_storage, custom Ed25519/P-256/EAX in tsclientlib, bundled TLS in rustls), so the value MUST be flipped back to <true/> with a valid ITSEncryptionExportComplianceCode before any public submission. docs/release/export-compliance.md (new) - Closed-source mass-market path under EAR §740.17(b)(1) with Note 3 to Category 5, Part 2 of the CCL (reclassifies the app from ECCN 5D002.c.1 to ECCN 5D992.c). - BIS annual self-classification report as CSV per Supplement No. 8 to Part 742, with authorization type MMKT; recipients crypt@bis.doc .gov + enc@nsa.gov; deadline Feb 1 for prior calendar year; no fee, no pre-approval, file within 30 days of first export. - Apple App Store Connect questionnaire answers for Chanora, with the supporting-documentation upload (sent BIS CSV + cover sheet). - Step-by-step revert plan once Apple issues the UUID, including the optional CHANORA_IOS_EXPORT_COMPLIANCE_CODE build-variable pattern (procivis/one-wallet style) to keep the UUID out of git. - Annual maintenance plus the fallback to CCATS / SNAP-R if Chanora ever adds non-standard crypto or E2EE DMs. - References to Apple docs, eCFR §740.17, Supplement No. 8, and real- world closed-source iOS Info.plist examples (Keybase, Status.im, Cryptomator) confirming the UUID format and key placement. Verification - plutil -lint apps/chanora_flutter/ios/Runner/Info.plist: OK - plutil -lint apps/chanora_flutter/macos/Runner/Info.plist: OK Follow-up - README.md / LICENSE-{APACHE,MIT} / DEC-020 still declare Chanora as Apache-2.0/MIT dual-licensed. The closed-source export-compliance doc assumes proprietary distribution; the license decision needs reconciliation before submission so the BIS CSV and Apple questionnaire answers match the actual distribution model.
11 KiB
iOS / macOS Export Compliance
Status: TestFlight workaround applied. Production submission requires the steps below.
Current state (2026-06-08)
Both apps/chanora_flutter/ios/Runner/Info.plist and apps/chanora_flutter/macos/Runner/Info.plist declare:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
This is a temporary workaround to unblock TestFlight uploads. It is NOT correct for public App Store release because Chanora ships non-exempt encryption (see "Why Chanora is not exempt" below). The workaround is acceptable for internal testing under TestFlight while the BIS self-classification and Apple export-compliance documentation are being prepared.
Why Chanora is not exempt
Chanora is a closed-source proprietary application that uses encryption beyond Apple's "standard system encryption" exemption:
chacha20poly1305incrates/chanora_storage— AEAD encryption of the local Data Encryption Key (DEK) for secret storage.tsclientlibincrates/chanora_protocolandcrates/chanora_audio— implements TeamSpeak's custom Ed25519 / P-256 / EAX cryptographic handshake. This is NOT standard IETF TLS.rustlsincrates/chanora_protocolandcrates/chanora_resolver— ships its own TLS implementation rather than using the system stack.
Apple's exemption (HTTPS via system networking only) does not apply. Because Chanora is closed-source, the "publicly available source code" path (§734.3(b)(3) + §742.15(b)) is also unavailable — that route is reserved for open-source distributions whose cryptographic source is publicly accessible without restriction.
Chanora's path is therefore the mass-market self-classification route under §740.17(b)(1) with Note 3 to Category 5, Part 2 of the CCL — closed-source consumer apps with standard published algorithms qualify as mass-market and self-classify under ECCN 5D992.c. ITSAppUsesNonExemptEncryption must be <true/> for any public release, and ITSEncryptionExportComplianceCode must accompany it.
The submission flow (in order)
Step 1: File the BIS annual self-classification report
US Department of Commerce, Bureau of Industry and Security (BIS), requires an annual self-classification report for mass-market encryption software under EAR §740.17(b)(1). Since Chanora is closed-source but distributed as a mass-market consumer app with standard cryptographic algorithms, it qualifies for self-classification under the mass-market note (Note 3 to Category 5, Part 2 of the CCL), which reclassifies it from ECCN 5D002.c.1 to ECCN 5D992.c.
There is no fee, no review, no pre-approval needed. You export immediately under (b)(1) and file the report within 30 days.
The format is a CSV file per Supplement No. 8 to Part 742, not a prose email body. Send it as an attachment:
Recipients (CC both):
crypt@bis.doc.govenc@nsa.gov
Subject: Self-Classification Report — Chanora — [YEAR]
CSV template (fill in [BRACKETS]):
Submitter Name,Submitter Email,Submitter Phone,Submitter Address,Item Type,Item Name,Item Version,ECCN,Authorization Type,Encryption Algorithm,Key Length,Item Description
[YOUR NAME],[YOU@EMAIL.COM],[PHONE],[ADDRESS],Mass Market Encryption Software,Chanora,[VERSION e.g. v1.0.0-rc.1],5D992.c,MMKT,ChaCha20-Poly1305; Ed25519; P-256 ECDH; EAX-AES; TLS 1.2/1.3,256,Cross-platform voice communication client for TeamSpeak-compatible servers. Cryptographic algorithms are all published standards; the application is closed-source proprietary software distributed via mass-market channels (Apple App Store).
Notes on the CSV:
- Authorization Type
MMKTsignals the mass-market self-classification under Note 3. - ECCN
5D992.c— the mass-market note reclassifies closed-source mass-market software with standard crypto from 5D002.c.1 to 5D992.c, removing it from "EI" and "NS" controls. - The CSV is a single row for one product. For multiple products, add one row per product.
- Send it once per calendar year (BIS deadline: February 1 for prior calendar year).
- Keep the sent-mail receipt — Apple may ask to see proof during the App Store review.
- BIS does not reply. Filing the report is the compliance event; no approval or confirmation is needed.
Step 2: App Store Connect — encryption questionnaire
Path: App Store Connect → My Apps → Chanora → App Information → Encryption (or during version submission, the same questionnaire appears).
Answer the questions as follows for Chanora:
- "Does your app use encryption?" → Yes
- "Does your app qualify for any of the exemptions provided in Category 5, Part 2 of the U.S. Export Administration Regulations?" → No (We do use non-exempt crypto — see "Why Chanora is not exempt".)
- "Does your app implement any encryption algorithms that are proprietary or not accepted as standard by international standard bodies (IEEE, IETF, ITU, etc.)?" → No (ChaCha20-Poly1305 is RFC 8439; Ed25519 is RFC 8032; P-256 is FIPS 186-4. TeamSpeak's handshake uses standard primitives even though the orchestration is custom.)
- "Does your app implement any standard encryption algorithms instead of, or in addition to, using or accessing the encryption in Apple's operating system?" → Yes
- "Is your app made available on the French App Store?" → answer per your distribution plan (Yes if you want EU/FR availability).
Apple will then ask you to upload supporting documentation. Upload:
- A PDF copy of your sent email from Step 1 (showing the date sent and both recipients), with the CSV attachment shown or attached.
- A short cover sheet identifying: product name (Chanora), ECCN (
5D992.c), authorization basis (§740.17(b)(1)mass-market self-classification under Note 3 to Category 5, Part 2 of the CCL), and the date the report was sent to BIS.
Step 3: Receive the code from Apple
After review (typical turnaround: a few business days to ~2 weeks), Apple sends an email with the export compliance code. The code is a UUID, lowercase, 36 characters, e.g. aeafaa12-a017-48c8-a29a-bdd3061f93e3 (this is Keybase's actual code, used here as a format example only).
The code is per-app, not per-version. Once issued it can be reused for every subsequent build until your encryption usage materially changes.
Step 4: Revert the workaround and patch in the code
Edit apps/chanora_flutter/ios/Runner/Info.plist:
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>PASTE_APPLE_ISSUED_UUID_HERE</string>
Do the same for apps/chanora_flutter/macos/Runner/Info.plist.
Optionally — to avoid committing the literal UUID to git — use a build variable instead (matches the procivis/one-wallet pattern):
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<key>ITSEncryptionExportComplianceCode</key>
<string>$(CHANORA_IOS_EXPORT_COMPLIANCE_CODE)</string>
Then set CHANORA_IOS_EXPORT_COMPLIANCE_CODE as a build setting (in Runner.xcodeproj user-defined settings, in a non-tracked .xcconfig, or via the CI/Fastlane environment).
Validate after editing:
plutil -lint apps/chanora_flutter/ios/Runner/Info.plist
plutil -lint apps/chanora_flutter/macos/Runner/Info.plist
Step 5: Submit and confirm
Re-upload the build via Xcode → Organizer → Distribute App, or via Fastlane / CI. App Store Connect should no longer reject the build with the "Invalid Export Compliance Code" error.
If you use Fastlane, you can also pass uses_non_exempt_encryption: true to upload_to_testflight / upload_to_app_store as belt-and-suspenders; the Info.plist value is authoritative.
Annual maintenance
- Every January, re-send the BIS self-classification CSV (Step 1) with the current year and the latest released version. Deadline: February 1 for the prior calendar year. Keep the receipt.
- The Apple-issued code does NOT need re-issuance unless Chanora adds a fundamentally new encryption capability (e.g. end-to-end encrypted DMs would qualify; bumping an existing AEAD library version would not).
- If you add E2EE for direct messages, change the at-rest encryption algorithm, or introduce proprietary (non-standard) cryptography, the mass-market classification may no longer apply — you would need to either re-self-classify or file a CCATS (Commodity Classification) request via SNAP-R, and Apple will need updated documentation and a new code.
References
- Apple —
ITSAppUsesNonExemptEncryptionkey: https://developer.apple.com/documentation/bundleresources/information-property-list/itsappusesnonexemptencryption - Apple —
ITSEncryptionExportComplianceCodekey: https://developer.apple.com/documentation/bundleresources/information-property-list/itsencryptionexportcompliancecode - Apple — Complying with encryption export regulations: https://developer.apple.com/documentation/security/complying-with-encryption-export-regulations
- Apple — App Store Connect Help, export compliance overview: https://developer.apple.com/help/app-store-connect/manage-app-information/overview-of-export-compliance/
- Apple — App Store Connect Help, determine and upload encryption documentation: https://developer.apple.com/help/app-store-connect/manage-app-information/determine-and-upload-app-encryption-documentation/
- BIS — Encryption FAQs: https://www.bis.doc.gov/index.php/policy-guidance/encryption
- BIS — EAR §740.17 (License Exception ENC, self-classification): https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-740/section-740.17
- BIS — Supplement No. 8 to Part 742 (self-classification report CSV format): https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-742
- BIS — Note 3 to Category 5, Part 2 of the CCL (mass-market note): https://www.ecfr.gov/current/title-15/subtitle-B/chapter-VII/subchapter-C/part-774
- Real-world examples (closed-source iOS apps with
ITSEncryptionExportComplianceCode): - Build-variable pattern (avoid committing UUID to git):
- Procivis One Wallet: https://github.com/procivis/one-wallet/blob/main/ios/Wallet/Info.plist
Decision log
| Date | Decision | Rationale |
|---|---|---|
| 2026-06-08 | Temporarily set ITSAppUsesNonExemptEncryption=<false/> on both iOS and macOS Info.plist |
Unblock TestFlight internal testing while BIS / Apple paperwork is being prepared. Re-do per Steps 1-5 above before any public App Store release. |