# Spellcheck engine and dictionary provenance This package ships a self-compiled WebAssembly build of Hunspell plus the LibreOffice/Ayaspell Arabic dictionary. This file records exact upstream revisions, the reproducible build process, checksums of every shipped asset, the applicable license path, and required notices, per D-032. ## 1. Hunspell (spellchecking engine) - **Upstream project:** [hunspell/hunspell](https://github.com/hunspell/hunspell) - **Exact version/tag used:** `v1.7.3` (the current stable release as of this writing, published 2026-05-05) - **Source tarball:** `hunspell-1.7.3.tar.gz`, downloaded directly from the official GitHub release asset at `https://github.com/hunspell/hunspell/releases/download/v1.7.3/hunspell-1.7.3.tar.gz` - **Source tarball SHA-256:** `433274dac0619cb00c2e18b43a3dd3a9d50da5b5613fa9b5c21781e35dd76bc1` (verified against the checksum GitHub's own Releases API reports for this exact asset before use) - **License:** MPL-1.1 / GPL-2.0-or-later / LGPL-2.1-or-later tri-license (see `src/hunspell/hunspell.h`'s own license header in the upstream source; the upstream repository's `COPYING`, `COPYING.LESSER`, and `COPYING.MPL` files carry the full license texts and are preserved unmodified alongside this notice — see §5) - **Copyright:** Kevin Hendricks (MySpell), Németh László (Hunspell), and the additional contributors listed in Hunspell's own source headers (`hunspell.h`) - **Files compiled:** `affentry.cxx`, `csutil.cxx`, `filemgr.cxx`, `hunspell.cxx`, `phonet.cxx`, `suggestmgr.cxx`, `affixmgr.cxx`, `hashmgr.cxx`, `hunzip.cxx`, `replist.cxx` — the same C++ source tree the upstream project itself builds `libhunspell` from, entirely unmodified (no patch applied to any source file) - **Vendored source copy:** the complete `src/hunspell/` source and header set from the exact tarball above (all 10 compiled `.cxx` files, every `.hxx`/`.h` header they include, 28 files, 3.2 MB) is copied byte-for-byte into `third-party-notices/hunspell/source/` in this package, with a SHA-256 checksum for every file recorded in that directory's own `CHECKSUMS.txt` (each independently re-verified against the freshly re-downloaded, re-checksummed tarball during this correction pass — see §3.1 below for why this exists, not just a link to GitHub) ### Build toolchain and reproducible command - **Compiler:** Emscripten `em++`, SDK version `6.0.9` (commit `4e4223852a0835923411059a3929907d7df1232e`), installed via the official `emsdk` (`emsdk install latest && emsdk activate latest` on the date this was built) - **Exact compile command** (run from the extracted `hunspell-1.7.3/src/hunspell` source directory's parent): ```sh em++ \ -s ALLOW_MEMORY_GROWTH=1 \ -s STACK_SIZE=1MB \ -s EXPORTED_FUNCTIONS="['_malloc','_free','_Hunspell_create','_Hunspell_create_key','_Hunspell_destroy','_Hunspell_add_dic','_Hunspell_spell','_Hunspell_get_dic_encoding','_Hunspell_suggest','_Hunspell_suffix_suggest','_Hunspell_analyze','_Hunspell_stem','_Hunspell_stem2','_Hunspell_generate','_Hunspell_generate2','_Hunspell_add','_Hunspell_add_with_flags','_Hunspell_add_with_affix','_Hunspell_remove','_Hunspell_free_list']" \ -s EXPORTED_RUNTIME_METHODS="['FS','lengthBytesUTF8','stringToUTF8','UTF8ToString','HEAPU32']" \ -s MODULARIZE=1 \ -s EXPORT_ES6=1 \ -s EXPORT_NAME="createHunspellModule" \ -s ENVIRONMENT=worker,node \ -O3 \ -o wasm/hunspell.js \ -I hunspell-1.7.3/src/hunspell \ hunspell-1.7.3/src/hunspell/{affentry,csutil,filemgr,hunspell,phonet,suggestmgr,affixmgr,hashmgr,hunzip,replist}.cxx ``` This produces `hunspell.js` (the Emscripten ES-module loader/glue) and `hunspell.wasm` (the compiled engine) as a matched pair. No `configure`/ autotools step was needed: the release tarball's own `src/hunspell/hunvisapi.h` (the one header autotools would otherwise generate from `hunvisapi.h.in`) already ships pre-resolved in the official release tarball, and Hunspell's optional Mozilla-integration code path (`#include "nscore.h"` in `csutil.hxx`) is compiled out entirely by the `#ifdef MOZILLA_CLIENT` guard already present in upstream source, since that macro is never defined here. ### Compatibility verification performed Before shipping, this exact build was cross-checked two independent ways against the identical Hunspell v1.7.3 source and the real, shipped `ar.aff`/`ar.dic` (see §2): 1. **A native (non-WASM) build** of the identical source tree, compiled with the same Emscripten-bundled `clang++` targeting the host directly (`clang++ -O2 -std=c++17`) rather than `wasm32-unknown-emscripten`. A small command-line harness loaded the real dictionary and ran `Hunspell_spell`/`Hunspell_suggest` over a test-word list exercising the Arabic affix file's `AF` (long-flag prefix/suffix classes), `IGNORE` (diacritics/tatweel), `MAP` (near-miss suggestion confusables), `ICONV` (input normalization, e.g. `ٱ` → `ا`), `REP` (common-typo replacement, e.g. `إست` → `است`), and cross-product prefix+suffix combination (e.g. `سيكتبون`, combining a `سي-` prefix and a `-ون` suffix on the same stem). The WASM build's output was byte-for-byte identical to the native build's output across every test word. 2. **Direct inspection of `ar.aff`'s own directive counts and content** (see §2) confirms the shipped Arabic dictionary genuinely exercises every directive class named above: `AF 333` (334 flag-class lines), 221 `PFX` blocks, 1609 `SFX` blocks, `IGNORE` (tanwīn/sukūn/tatweel), 17 `MAP` lines (16 groups), 10 `ICONV` lines, 81 `REP` lines, and `FLAG long`. No compatibility blocker was found; this Hunspell v1.7.3 WASM build is considered fully compatible with the shipped Ayaspell Arabic dictionary for this product's purposes (spell/suggest via the standard `Hunspell_spell`/ `Hunspell_suggest` C API). ## 2. Ayaspell / LibreOffice Arabic dictionary - **Upstream project:** [linuxscout/ayaspell](https://github.com/linuxscout/ayaspell), distributed to this product via its mirror in [LibreOffice/dictionaries](https://github.com/LibreOffice/dictionaries) (the `ar/` directory) — the same distribution channel D-032 names - **Exact revision fetched from:** `LibreOffice/dictionaries` commit `32b006a2c22a4ac7e8ed3f03346f7b3d85a970a4` (the repository's `master` HEAD on the date these assets were fetched), path `ar/` - **Dictionary content itself last substantively changed** in that repository's own commit `476ebd97bbfdcf59f00cd832647b880ddfdfb252` ("Update arabic dictionary to version 3.5 (2014-11-08)"), which states it extracted the files from the upstream Ayaspell release `dict_ar-3.5.2014-11-08.oxt` (`http://sourceforge.net/projects/ayaspell/files/dict_ar-3.5.2014-11-08.oxt/download`). This is a real, disclosed limitation, not something this task can correct by itself: as of the revision fetched, the LibreOffice-distributed Ayaspell Arabic dictionary has not been updated since 2018 (content dated 2014), and no more recent maintained upstream revision was found. See the release report's "genuine remaining limitations" section. - **License:** GPL-2.0-or-later / LGPL-2.1-or-later / MPL-1.1-or-later tri-license, per `ar/COPYING.txt` in the fetched revision (verbatim header: "GPL 2.0/LGPL 2.1/MPL 1.1 tri-license") - **Copyright:** - Hunspell-ar, the Arabic Hunspell dictionary itself: © 2006–2008, Mohamed Kebdani (`med.kebdani@gmail.com`) - Packaging for OpenOffice.org 3.0 (the `.oxt`/LibreOffice distribution this file set was extracted from): Ahmad Farghal (`ahmad.farghal@gmail.com`), per `ar/README_ar.txt`, itself crediting the upstream Ayaspell project (`http://ayaspell.sourceforge.net/`) - (The companion Arabic thesaurus, `th_ar.dat`, © 2006–2009 Taha Zerrouki, is a separate asset **not shipped by this product** — this feature is spellchecking only, no thesaurus/synonym lookup.) ### Shipped files and checksums Deployed identically (byte-for-byte) to `apps/arabickeyboard/public/spellcheck/` and `apps/clavierarabe/public/spellcheck/`. | File | Bytes | SHA-256 | | --------------- | --------- | ------------------------------------------------------------------ | | `ar.aff` | 86,949 | `cec30b8621001e49618feb05aec1984c5fcfbf7d2ec309901d5cbf66585217a3` | | `ar.dic` | 7,217,161 | `2a3e5367f61c1583734db9d66734f5603e6be5c2d227cf5c5cd7e4ca586e34fe` | | `hunspell.wasm` | 765,901 | `54c4c2aecf1353e77e01eecf145031f12af6a3b4cdcfeb2a4066fcfb19922e18` | | `hunspell.js` | 62,580 | `9ee7ade8d96f8e4a93ec18e6b0b4acb2fc5e7337d327b78019c8ccc33bea8258` | ## 3. Selected license path Because both Hunspell (§1) and the Ayaspell/LibreOffice Arabic dictionary (§2) are each available under the identical GPL-2.0-or-later / LGPL-2.1-or-later / MPL-1.1(-or-later) tri-license, this product selects the **Mozilla Public License 1.1 (MPL-1.1)** as the single applicable path for both, for these reasons: - MPL is a file-level (not whole-program) copyleft license: it requires that modifications to the _covered files themselves_ remain available under the MPL, but does not require this product's own separate application source code (the rest of this monorepo) to be relicensed — the covered files here (`ar.aff`, `ar.dic`, and the unmodified Hunspell `.cxx`/`.hxx` sources compiled into `hunspell.wasm`) are shipped as static data/compiled-WASM assets, not linked/modified application source. - No source file in either component was modified from its upstream form (see §1's "unmodified" note and this file's own build command, which compiles the release tarball's files directly with no patch step) — so there is no "modified file" to mark or additionally disclose under MPL §3.3's modification-marking requirement. - The MPL path avoids the GPL's copyleft reaching into this product's separately-licensed application code (a concern the LGPL's own weaker "linking" exception was written for library-linking scenarios that don't cleanly describe "compiled to a static WASM asset served over HTTP" the way MPL's plain file-level scope does). ### What this requires this product to do, and what it has done - **Preserve copyright/license notices:** done — this file (§1–§2) records every copyright holder and the applicable license, and the upstream license text files themselves (`COPYING`, `COPYING.LESSER`, `COPYING.MPL` from the Hunspell release; `ar/COPYING.txt` from the Ayaspell/LibreOffice revision) are copied unmodified into this package — see §5. - **Provide the covered source, self-hosted — not merely link to it (§3.1):** an earlier draft of this notice named only the upstream GitHub release URL and LibreOffice/Ayaspell repository as "where the source can be obtained." That is not, by itself, sufficient: MPL §3.2 requires the distributor - this product - to make Source Code Form available for at least the applicable disclosure period, and a link to a third party's repository that this product does not control (GitHub, LibreOffice) does not satisfy that obligation on its own - the upstream project could rename, delete, or move the release at any time, entirely outside this product's control. This is corrected: the exact `src/hunspell/` source tree actually compiled (28 files, all 10 `.cxx` files plus every header they include) is vendored byte-for-byte into `third-party-notices/hunspell/source/` in this package (checksums in that directory's own `CHECKSUMS.txt`, independently re-verified against a freshly re-downloaded copy of the same tarball during this correction pass) and committed to this project's own source control alongside the compiled `.wasm`/`.js` output, so it ships and persists for exactly as long as this product's own source code does - not contingent on any third party's continued hosting. `ar.aff`/`ar.dic` (§2) are themselves already Source Code Form (plain text, not compiled), and are already shipped as-is; a separate `ayaspell/source/` copy is therefore unnecessary - the shipped `ar.aff`/`ar.dic` files (§2's checksums) already are the covered source. - **State exactly where the corresponding source and build instructions can be obtained:** done, twice over - both the upstream location (§1/§2, for provenance/attribution) and the self-hosted vendored copy this product actually controls (immediately above), plus the exact, reproducible compile command (this section's own "Build toolchain and reproducible command" above) needed to turn the vendored source back into the shipped `.wasm`/`.js` output. - **No file was stripped of its own header/attribution:** `ar.aff`'s own file-level comments and `ar.dic`'s own content are shipped exactly as fetched from the pinned commit (see §2's checksums); Hunspell's own per-file MPL/GPL/LGPL license header block (visible at the top of `hunspell.h`, `hunspell.hxx`, etc. in the upstream source) is unmodified in both the vendored source copy and the source used to compile `hunspell.wasm`. - **Do not claim a different license for the compiled `.wasm`/`.js` output:** the compiled artifacts (`hunspell.wasm`, `hunspell.js`) are a direct, mechanical compilation of the unmodified upstream C++ source and remain covered by the same MPL-1.1 terms as that source. #### §3.1 A conspicuous, publicly reachable copy of this notice This file lives in this project's own source repository, which is not by itself something an end user visiting either live domain can discover or reach. Both `/legal-notice` (ArabicKeyboard.ai) and `/mentions-legales` (ClavierArabe.ai) - the same pages that already carry this product's other required legal disclosures - now include a dedicated "Open-source components" section naming Hunspell and Ayaspell, their licenses, and linking to this file's own path in this project's public GitHub repository (`https://github.com/muela002-arc/arabic-keyboard-sites`), so the source-and-license notice is reachable from both live domains, not only from this repository. That link necessarily only resolves once this D-032 correction pass is itself committed and pushed - true of any self-referential in-repo link a still-unstaged change makes to itself; this is disclosed here rather than hidden, and is expected to already be true by the time this page is live in production. ## 4. Build/runtime dependency licenses - **Emscripten** — the compiler/toolchain (SDK `6.0.9`) used to produce `hunspell.wasm`/`hunspell.js`. The Emscripten _toolchain installation itself_ (the compiler binaries, `emsdk`) is not shipped to end users - it runs only at build time, on the machine that compiles this package's assets. This is a real, meaningful distinction, but an earlier draft of this notice stated it imprecisely as "Emscripten... not itself shipped," which reads as though nothing Emscripten-derived reaches the browser at all - that is false and is corrected here: `hunspell.js`, the shipped glue/loader every browser and worker actually downloads and runs (see `checksums.json`), is not purely Hunspell's own code - it is Emscripten's own generated JavaScript runtime support (module instantiation, memory management, the WASM-import glue) wrapped around the compiled Hunspell calls, and does ship to every end user. Emscripten is available under a choice of the MIT license and the University of Illinois/NCSA Open Source License (both permissive) - see the exact, verbatim license text fetched directly from the upstream project's own `LICENSE` file (`https://github.com/emscripten-core/emscripten/blob/main/LICENSE`, fetched during this correction pass) preserved unmodified at `third-party-notices/emscripten/LICENSE` - see §5. - No other third-party runtime dependency is compiled into or shipped alongside these assets — `packages/spellcheck`'s own TypeScript source (worker/client/tokenizer-adjacent logic) has zero npm runtime dependencies of its own (see its `package.json`) ## 5. Preserved upstream license/notice files The following files are copied, unmodified, into this package's own `third-party-notices/` directory (byte-for-byte from the exact revisions named in §1/§2 above), so a recipient of this product's source never needs to fetch them separately to see the full license text: - `third-party-notices/hunspell/COPYING` (GPL-2.0, from the Hunspell v1.7.3 release tarball) - `third-party-notices/hunspell/COPYING.LESSER` (LGPL-2.1, from the same) - `third-party-notices/hunspell/COPYING.MPL` (MPL-1.1, from the same) - `third-party-notices/hunspell/source/*.cxx`, `*.hxx`, `*.h` (the complete, vendored, byte-for-byte source this package actually compiles - see §3.1 for why this is vendored, not merely linked), plus that directory's own `CHECKSUMS.txt` - `third-party-notices/ayaspell/COPYING.txt` (the Ayaspell/LibreOffice Arabic dictionary's own tri-license notice, from the pinned commit) - `third-party-notices/ayaspell/AUTHORS.txt` (dictionary/thesaurus copyright holders, from the pinned commit) - `third-party-notices/ayaspell/README_ar.txt` (packaging provenance note, from the pinned commit) - `third-party-notices/emscripten/LICENSE` (the MIT/University of Illinois-NCSA dual-license text covering the Emscripten-generated runtime support shipped inside `hunspell.js` - see §4) ## 6. How the engine is used `packages/spellcheck` uses Hunspell exclusively via its standard public C API (`Hunspell_create`, `Hunspell_spell`, `Hunspell_suggest`, `Hunspell_free_list`) to determine whether a tokenized Arabic word is correctly spelled and, if not, to obtain a ranked list of dictionary -derived correction candidates. It never generates a candidate the dictionary itself did not already produce (see `arabicRanker.ts`'s own header for the one narrow, separately-reviewed exception — `trustedReplacements.ts` — and `07 Decisions.md`'s D-032 entry for the product-level guardrails around when/whether a candidate is ever applied automatically).